kavinkrisher commited on
Commit
41cc9b3
Β·
1 Parent(s): ef82f40

Replace hardcoded baseline with live Groq LLM agent

Browse files
Files changed (1) hide show
  1. 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 hardcoded expert baseline β€” watch every action live as it executes")
559
 
560
- run_btn = gr.Button("β–Ά Run Full Baseline (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 baseline agent…\n\nYou will see each action appear here in real-time as the agent\nworks through all 4 stages of the pipeline.",
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("β•‘ PipelineOps Arena β€” Baseline Run β•‘")
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
- steps = [
607
- # Stage 1
608
- {"action_type": "inspect_data", "payload": {}},
609
- {"action_type": "edit_script", "payload": {
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", data)
656
- rwd = data.get("reward", {})
657
- score = float(rwd.get("score", 0.0))
658
- msg = rwd.get("message", "")[:55]
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.4)
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"β•‘ βœ… EPISODE COMPLETE! β•‘")
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 baseline run: {e}")
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