Spaces:
Sleeping
Sleeping
Commit Β·
41cc9b3
1
Parent(s): ef82f40
Replace hardcoded baseline with live Groq LLM agent
Browse files- gradio_app.py +133 -51
gradio_app.py
CHANGED
|
@@ -555,15 +555,15 @@ with gr.Blocks(title="PipelineOps Arena β OpenEnv") as demo:
|
|
| 555 |
|
| 556 |
# ββ BASELINE TAB ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 557 |
with gr.Tab("π Run Baseline"):
|
| 558 |
-
gr.Markdown("### Run the
|
| 559 |
|
| 560 |
-
run_btn = gr.Button("βΆ Run
|
| 561 |
|
| 562 |
live_output = gr.Textbox(
|
| 563 |
label="β‘ Live Action Log",
|
| 564 |
lines=28,
|
| 565 |
interactive=False,
|
| 566 |
-
placeholder="Click Run to start the
|
| 567 |
elem_classes="code-box"
|
| 568 |
)
|
| 569 |
|
|
@@ -584,6 +584,8 @@ with gr.Blocks(title="PipelineOps Arena β OpenEnv") as demo:
|
|
| 584 |
|
| 585 |
def run_baseline_streaming():
|
| 586 |
import time as _time
|
|
|
|
|
|
|
| 587 |
log = []
|
| 588 |
|
| 589 |
def emit(line):
|
|
@@ -591,11 +593,80 @@ with gr.Blocks(title="PipelineOps Arena β OpenEnv") as demo:
|
|
| 591 |
return "\n".join(log)
|
| 592 |
|
| 593 |
yield emit("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ")
|
| 594 |
-
yield emit("β
|
| 595 |
yield emit("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n")
|
| 596 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 597 |
try:
|
| 598 |
-
# Reset
|
| 599 |
r = httpx.post(f"{BASE_URL}/reset", timeout=15)
|
| 600 |
obs = r.json()
|
| 601 |
if "observation" in obs:
|
|
@@ -603,46 +674,47 @@ with gr.Blocks(title="PipelineOps Arena β OpenEnv") as demo:
|
|
| 603 |
yield emit(f"β
Environment reset β Stage 1: {STAGE_LABELS[1]}\n")
|
| 604 |
_time.sleep(0.3)
|
| 605 |
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
"old": "X = df[['age_years', 'salary', 'credit_score', 'loan_amount', 'employment_years']].copy()",
|
| 611 |
-
"new": "df.dropna(inplace=True)\nX = df[['age', 'salary', 'credit_score', 'loan_amount', 'employment_years']].copy()"
|
| 612 |
-
}},
|
| 613 |
-
{"action_type": "run_script", "payload": {}},
|
| 614 |
-
{"action_type": "submit", "payload": {}},
|
| 615 |
-
# Stage 2
|
| 616 |
-
{"action_type": "inspect_data", "payload": {}},
|
| 617 |
-
{"action_type": "edit_script", "payload": {
|
| 618 |
-
"old": "clf = MLPClassifier(hidden_layer_sizes=(64, 32), max_iter=200, random_state=42)",
|
| 619 |
-
"new": "from sklearn.preprocessing import StandardScaler\nscaler = StandardScaler()\nX_train = scaler.fit_transform(X_train)\nX_test = scaler.transform(X_test)\nclf = MLPClassifier(hidden_layer_sizes=(64, 32), max_iter=200, random_state=42)"
|
| 620 |
-
}},
|
| 621 |
-
{"action_type": "run_script", "payload": {}},
|
| 622 |
-
{"action_type": "submit", "payload": {}},
|
| 623 |
-
# Stage 3
|
| 624 |
-
{"action_type": "run_script", "payload": {}},
|
| 625 |
-
{"action_type": "edit_script", "payload": {
|
| 626 |
-
"old": "scaler = StandardScaler()\nX_scaled = scaler.fit_transform(X)\nX_train, X_test, y_train, y_test = train_test_split(\n X_scaled, y, test_size=0.2, random_state=42, stratify=y\n)",
|
| 627 |
-
"new": "X_train, X_test, y_train, y_test = train_test_split(\n X, y, test_size=0.2, random_state=42, stratify=y\n)\nscaler = StandardScaler()\nX_train = scaler.fit_transform(X_train)\nX_test = scaler.transform(X_test)"
|
| 628 |
-
}},
|
| 629 |
-
{"action_type": "run_script", "payload": {}},
|
| 630 |
-
{"action_type": "submit", "payload": {}},
|
| 631 |
-
# Stage 4
|
| 632 |
-
{"action_type": "inspect_data", "payload": {}},
|
| 633 |
-
{"action_type": "query_actor", "payload": {}},
|
| 634 |
-
{"action_type": "edit_script", "payload": {
|
| 635 |
-
"old": "clf = LogisticRegression(max_iter=1000, random_state=42)",
|
| 636 |
-
"new": "clf = LogisticRegression(max_iter=1000, random_state=42, class_weight='balanced')"
|
| 637 |
-
}},
|
| 638 |
-
{"action_type": "run_script", "payload": {}},
|
| 639 |
-
{"action_type": "submit", "payload": {}},
|
| 640 |
-
]
|
| 641 |
|
| 642 |
current_stage = 1
|
| 643 |
step_num = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 644 |
|
| 645 |
-
for action in steps:
|
| 646 |
step_num += 1
|
| 647 |
atype = action["action_type"]
|
| 648 |
icon = ACTION_ICONS.get(atype, "βΈ")
|
|
@@ -652,45 +724,55 @@ with gr.Blocks(title="PipelineOps Arena β OpenEnv") as demo:
|
|
| 652 |
|
| 653 |
resp = httpx.post(f"{BASE_URL}/step", json=action, timeout=30)
|
| 654 |
data = resp.json()
|
| 655 |
-
obs = data.get("observation",
|
| 656 |
-
|
| 657 |
-
score = float(
|
| 658 |
-
msg =
|
| 659 |
new_stage = obs.get("current_stage", current_stage)
|
| 660 |
done = obs.get("done", False)
|
| 661 |
|
| 662 |
-
# Overwrite the last line with result
|
| 663 |
log[-1] = f" Step {step_num:02d} β {icon} {atype.upper():<18} β score={score:.2f} {msg}"
|
| 664 |
yield "\n".join(log)
|
| 665 |
|
| 666 |
-
# Stage transition banner
|
| 667 |
if new_stage != current_stage:
|
| 668 |
current_stage = new_stage
|
|
|
|
| 669 |
label = STAGE_LABELS.get(current_stage, f"Stage {current_stage}")
|
| 670 |
yield emit(f"\n{'β'*54}")
|
| 671 |
yield emit(f" β Advancing to Stage {current_stage}: {label}")
|
| 672 |
yield emit(f"{'β'*54}\n")
|
| 673 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 674 |
if done:
|
| 675 |
break
|
| 676 |
|
| 677 |
-
_time.sleep(0.
|
| 678 |
|
| 679 |
-
# Final summary
|
| 680 |
status = httpx.get(f"{BASE_URL}/pipeline_status", timeout=10).json()
|
| 681 |
completed = status.get("stages_completed", [])
|
| 682 |
final_score = status.get("episode_score", 0.0)
|
| 683 |
total_steps = status.get("total_steps", step_num)
|
| 684 |
|
| 685 |
yield emit(f"\nβββββββββββββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½οΏ½ββ")
|
| 686 |
-
yield emit(f"β
|
| 687 |
yield emit(f"β Stages completed : {str(completed):<34}β")
|
| 688 |
yield emit(f"β Total steps : {str(total_steps):<34}β")
|
| 689 |
yield emit(f"β Final score : {final_score:.2f}{'':<33}β")
|
| 690 |
yield emit(f"ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ")
|
| 691 |
|
| 692 |
except Exception as e:
|
| 693 |
-
yield emit(f"\nβ Error during
|
| 694 |
|
| 695 |
run_btn.click(run_baseline_streaming, inputs=[], outputs=[live_output])
|
| 696 |
|
|
|
|
| 555 |
|
| 556 |
# ββ BASELINE TAB ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 557 |
with gr.Tab("π Run Baseline"):
|
| 558 |
+
gr.Markdown("### Run the live LLM agent β Llama 3.1 reasons through each stage in real time")
|
| 559 |
|
| 560 |
+
run_btn = gr.Button("βΆ Run LLM Agent (All 4 Stages)", elem_classes="btn-primary")
|
| 561 |
|
| 562 |
live_output = gr.Textbox(
|
| 563 |
label="β‘ Live Action Log",
|
| 564 |
lines=28,
|
| 565 |
interactive=False,
|
| 566 |
+
placeholder="Click Run to start the LLM agentβ¦\n\nThe agent reads the error logs and script, reasons about the bug,\nand generates fix actions entirely on its own.",
|
| 567 |
elem_classes="code-box"
|
| 568 |
)
|
| 569 |
|
|
|
|
| 584 |
|
| 585 |
def run_baseline_streaming():
|
| 586 |
import time as _time
|
| 587 |
+
import os as _os
|
| 588 |
+
import re as _re
|
| 589 |
log = []
|
| 590 |
|
| 591 |
def emit(line):
|
|
|
|
| 593 |
return "\n".join(log)
|
| 594 |
|
| 595 |
yield emit("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ")
|
| 596 |
+
yield emit("β PipelineOps Arena β Live LLM Agent Run β")
|
| 597 |
yield emit("ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n")
|
| 598 |
|
| 599 |
+
groq_key = _os.environ.get("GROQ_API_KEY", "")
|
| 600 |
+
if not groq_key:
|
| 601 |
+
yield emit("β GROQ_API_KEY not set. Add it to the HF Space secrets.")
|
| 602 |
+
return
|
| 603 |
+
|
| 604 |
+
try:
|
| 605 |
+
from groq import Groq
|
| 606 |
+
client = Groq(api_key=groq_key)
|
| 607 |
+
except ImportError:
|
| 608 |
+
yield emit("β groq package not installed. Add 'groq' to requirements.txt.")
|
| 609 |
+
return
|
| 610 |
+
|
| 611 |
+
SYSTEM_PROMPT = """You are debugging a broken ML pipeline. Fix it fast.
|
| 612 |
+
|
| 613 |
+
RESPOND WITH ONLY A JSON OBJECT. No explanation. No markdown.
|
| 614 |
+
|
| 615 |
+
Actions:
|
| 616 |
+
1. {"action_type": "inspect_data", "payload": {}}
|
| 617 |
+
2. {"action_type": "run_script", "payload": {}}
|
| 618 |
+
3. {"action_type": "edit_script", "payload": {"old": "exact line from script", "new": "replacement"}}
|
| 619 |
+
4. {"action_type": "edit_script", "payload": {"script": "FULL corrected script here"}}
|
| 620 |
+
5. {"action_type": "query_actor", "payload": {}}
|
| 621 |
+
6. {"action_type": "submit", "payload": {}}
|
| 622 |
+
|
| 623 |
+
STRATEGY:
|
| 624 |
+
1. Inspect data OR run script ONCE to see the error
|
| 625 |
+
2. Use edit_script to fix the bug β prefer option 4 (full script) if unsure about exact text match
|
| 626 |
+
3. Run script ONCE to verify fix
|
| 627 |
+
4. Submit immediately if no error
|
| 628 |
+
|
| 629 |
+
RULES:
|
| 630 |
+
- Maximum 5 actions per stage
|
| 631 |
+
- NEVER inspect more than once, NEVER run more than twice
|
| 632 |
+
- Submit as soon as output looks correct
|
| 633 |
+
- If you see KeyError 'age_years': rename to 'age' and add df.dropna(inplace=True) before feature selection
|
| 634 |
+
- If loss is NaN: add StandardScaler before classifier
|
| 635 |
+
- If accuracy suspiciously high (>0.95): move scaler.fit() to AFTER train_test_split()
|
| 636 |
+
- If fairness issue: add class_weight='balanced' to classifier"""
|
| 637 |
+
|
| 638 |
+
def parse_action(raw_text):
|
| 639 |
+
match = _re.search(r'\{[^{}]*("payload"\s*:\s*\{[^{}]*\})?[^{}]*\}', raw_text, _re.DOTALL)
|
| 640 |
+
if match:
|
| 641 |
+
try:
|
| 642 |
+
action = json.loads(match.group().replace("'", '"'))
|
| 643 |
+
if "action_type" in action:
|
| 644 |
+
action.setdefault("payload", {})
|
| 645 |
+
return action
|
| 646 |
+
except json.JSONDecodeError:
|
| 647 |
+
pass
|
| 648 |
+
for atype in ["edit_script", "run_script", "submit", "inspect_data", "query_actor"]:
|
| 649 |
+
if atype in raw_text:
|
| 650 |
+
return {"action_type": atype, "payload": {}}
|
| 651 |
+
return {"action_type": "run_script", "payload": {}}
|
| 652 |
+
|
| 653 |
+
def format_obs(obs, reward=None):
|
| 654 |
+
parts = [f"Stage {obs.get('current_stage','?')} | Step {obs.get('stage_step_number','?')}"]
|
| 655 |
+
if obs.get("last_run_error"):
|
| 656 |
+
parts.append(f"\nERROR:\n{str(obs['last_run_error'])[:400]}")
|
| 657 |
+
if obs.get("last_run_output"):
|
| 658 |
+
parts.append(f"\nOUTPUT:\n{str(obs['last_run_output'])[:300]}")
|
| 659 |
+
if obs.get("data_preview"):
|
| 660 |
+
parts.append(f"\nDATA:\n{str(obs['data_preview'])[:400]}")
|
| 661 |
+
if obs.get("actor_feedback"):
|
| 662 |
+
parts.append(f"\nREVIEWER:\n{str(obs['actor_feedback'])[:300]}")
|
| 663 |
+
if obs.get("script_content"):
|
| 664 |
+
parts.append(f"\nSCRIPT:\n{obs['script_content'][:800]}")
|
| 665 |
+
if reward:
|
| 666 |
+
parts.append(f"\nREWARD: {reward.get('score',0):.2f} | {reward.get('message','')}")
|
| 667 |
+
return "\n".join(parts)
|
| 668 |
+
|
| 669 |
try:
|
|
|
|
| 670 |
r = httpx.post(f"{BASE_URL}/reset", timeout=15)
|
| 671 |
obs = r.json()
|
| 672 |
if "observation" in obs:
|
|
|
|
| 674 |
yield emit(f"β
Environment reset β Stage 1: {STAGE_LABELS[1]}\n")
|
| 675 |
_time.sleep(0.3)
|
| 676 |
|
| 677 |
+
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
|
| 678 |
+
initial = format_obs(obs)
|
| 679 |
+
initial += "\n\nStart by inspecting the data, then fix the bug. What is your first action?"
|
| 680 |
+
messages.append({"role": "user", "content": initial})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
|
| 682 |
current_stage = 1
|
| 683 |
step_num = 0
|
| 684 |
+
action_history = []
|
| 685 |
+
|
| 686 |
+
for _ in range(60):
|
| 687 |
+
try:
|
| 688 |
+
trimmed = [messages[0], messages[1]] + messages[-4:] if len(messages) > 8 else messages
|
| 689 |
+
response = client.chat.completions.create(
|
| 690 |
+
model="llama-3.1-8b-instant",
|
| 691 |
+
messages=trimmed,
|
| 692 |
+
max_tokens=800,
|
| 693 |
+
temperature=0.2,
|
| 694 |
+
)
|
| 695 |
+
raw = response.choices[0].message.content.strip()
|
| 696 |
+
action = parse_action(raw)
|
| 697 |
+
except Exception as e:
|
| 698 |
+
yield emit(f" LLM error: {e}")
|
| 699 |
+
action = {"action_type": "run_script", "payload": {}}
|
| 700 |
+
|
| 701 |
+
# Anti-loop guards
|
| 702 |
+
action_history.append(action["action_type"])
|
| 703 |
+
if len(action_history) >= 2 and action_history[-2] == "submit" and action["action_type"] == "submit":
|
| 704 |
+
action = {"action_type": "edit_script", "payload": {"script": obs.get("script_content", "")}}
|
| 705 |
+
if len(action_history) >= 4:
|
| 706 |
+
last4 = action_history[-4:]
|
| 707 |
+
if last4 in [["edit_script", "run_script", "edit_script", "run_script"],
|
| 708 |
+
["run_script", "edit_script", "run_script", "edit_script"]]:
|
| 709 |
+
action = {"action_type": "submit", "payload": {}}
|
| 710 |
+
if len(action_history) >= 3 and len(set(action_history[-3:])) == 1:
|
| 711 |
+
if action_history[-1] == "inspect_data":
|
| 712 |
+
action = {"action_type": "edit_script", "payload": {"script": obs.get("script_content", "")}}
|
| 713 |
+
elif action_history[-1] == "submit":
|
| 714 |
+
action = {"action_type": "run_script", "payload": {}}
|
| 715 |
+
else:
|
| 716 |
+
action = {"action_type": "submit", "payload": {}}
|
| 717 |
|
|
|
|
| 718 |
step_num += 1
|
| 719 |
atype = action["action_type"]
|
| 720 |
icon = ACTION_ICONS.get(atype, "βΈ")
|
|
|
|
| 724 |
|
| 725 |
resp = httpx.post(f"{BASE_URL}/step", json=action, timeout=30)
|
| 726 |
data = resp.json()
|
| 727 |
+
obs = data.get("observation", {})
|
| 728 |
+
reward = data.get("reward", {})
|
| 729 |
+
score = float(reward.get("score", 0.0))
|
| 730 |
+
msg = reward.get("message", "")[:55]
|
| 731 |
new_stage = obs.get("current_stage", current_stage)
|
| 732 |
done = obs.get("done", False)
|
| 733 |
|
|
|
|
| 734 |
log[-1] = f" Step {step_num:02d} β {icon} {atype.upper():<18} β score={score:.2f} {msg}"
|
| 735 |
yield "\n".join(log)
|
| 736 |
|
|
|
|
| 737 |
if new_stage != current_stage:
|
| 738 |
current_stage = new_stage
|
| 739 |
+
action_history = []
|
| 740 |
label = STAGE_LABELS.get(current_stage, f"Stage {current_stage}")
|
| 741 |
yield emit(f"\n{'β'*54}")
|
| 742 |
yield emit(f" β Advancing to Stage {current_stage}: {label}")
|
| 743 |
yield emit(f"{'β'*54}\n")
|
| 744 |
|
| 745 |
+
messages.append({"role": "assistant", "content": json.dumps(action)})
|
| 746 |
+
next_prompt = format_obs(obs, reward)
|
| 747 |
+
if atype == "inspect_data":
|
| 748 |
+
next_prompt += "\n\nData inspected. Now use edit_script to fix the bug."
|
| 749 |
+
elif atype == "run_script" and obs.get("last_run_error"):
|
| 750 |
+
next_prompt += '\n\nScript has error. Use edit_script with full script rewrite: {"action_type":"edit_script","payload":{"script":"..."}}'
|
| 751 |
+
elif atype == "run_script" and not obs.get("last_run_error"):
|
| 752 |
+
next_prompt += '\n\nScript ran with no error! Submit now: {"action_type":"submit","payload":{}}'
|
| 753 |
+
elif atype == "edit_script":
|
| 754 |
+
next_prompt += '\n\nEdited. Run script to verify: {"action_type":"run_script","payload":{}}'
|
| 755 |
+
messages.append({"role": "user", "content": next_prompt})
|
| 756 |
+
|
| 757 |
if done:
|
| 758 |
break
|
| 759 |
|
| 760 |
+
_time.sleep(0.3)
|
| 761 |
|
|
|
|
| 762 |
status = httpx.get(f"{BASE_URL}/pipeline_status", timeout=10).json()
|
| 763 |
completed = status.get("stages_completed", [])
|
| 764 |
final_score = status.get("episode_score", 0.0)
|
| 765 |
total_steps = status.get("total_steps", step_num)
|
| 766 |
|
| 767 |
yield emit(f"\nβββββββββββββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½οΏ½ββ")
|
| 768 |
+
yield emit(f"β EPISODE COMPLETE β")
|
| 769 |
yield emit(f"β Stages completed : {str(completed):<34}β")
|
| 770 |
yield emit(f"β Total steps : {str(total_steps):<34}β")
|
| 771 |
yield emit(f"β Final score : {final_score:.2f}{'':<33}β")
|
| 772 |
yield emit(f"ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ")
|
| 773 |
|
| 774 |
except Exception as e:
|
| 775 |
+
yield emit(f"\nβ Error during agent run: {e}")
|
| 776 |
|
| 777 |
run_btn.click(run_baseline_streaming, inputs=[], outputs=[live_output])
|
| 778 |
|