Spaces:
Sleeping
Sleeping
File size: 7,963 Bytes
e9c3076 37b054e e9c3076 37b054e e9c3076 37b054e e9c3076 37b054e e9c3076 37b054e e9c3076 37b054e e9c3076 516d2c6 37b054e 516d2c6 37b054e 516d2c6 1f6c7ae 516d2c6 1f6c7ae 516d2c6 1f6c7ae 516d2c6 1f6c7ae 516d2c6 1f6c7ae b761978 1f6c7ae 516d2c6 4fd3038 e9c3076 | 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 190 191 192 193 194 | #!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
"""
Baseline inference script for the Self-Healing DevOps Sandbox.
Uses an LLM (via the OpenAI-compatible API) to diagnose and fix a broken
Node.js backend running inside a Docker container.
"""
import json
import os
import sys
try:
from openai import OpenAI
except ImportError:
print("ERROR: 'openai' package is required. Install with: pip install openai")
sys.exit(1)
from client import DevopsSandboxEnv
from models import BashAction
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
API_BASE_URL = os.getenv("API_BASE_URL") or "https://router.huggingface.co/v1"
MODEL_NAME = os.getenv("MODEL_NAME") or "gpt-4o-mini"
HF_TOKEN = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
ENV_URL = os.getenv("DEVOPS_SANDBOX_URL", "http://localhost:8000")
TASK_NAME = os.getenv("MY_ENV_V4_TASK", "devops_sandbox")
BENCHMARK = os.getenv("MY_ENV_V4_BENCHMARK", "devops_sandbox")
MAX_TURNS = int(os.getenv("MAX_TURNS", "8"))
SYSTEM_PROMPT = """\
You are an expert DevOps engineer and Node.js developer.
You have been dropped into a Linux container with a broken Express.js backend in /app.
Your goal is to diagnose and fix ALL bugs so the app runs correctly.
RULES:
1. Respond ONLY with a JSON object: {"command": "<bash command>"}
2. Use standard bash/Linux commands (ls, cat, grep, sed, node, npm, etc.)
3. Do NOT use interactive editors (vi, nano). Use sed or echo/cat with redirection.
4. After fixing bugs, restart the app with: cd /app && npm start &
5. Be methodical: read files first, understand the bug, then fix it.
EXPECTED FINAL STATE:
- App starts without errors on port 3000
- GET /health → 200
- GET /api/users → 200 with JSON containing "users" array
- GET /api/data → 200 with JSON containing "records" array
"""
def extract_command(llm_response: str) -> str:
"""Extract a bash command from the LLM's response (JSON or raw text)."""
try:
data = json.loads(llm_response.strip())
if isinstance(data, dict) and "command" in data:
return data["command"]
except (json.JSONDecodeError, TypeError):
pass
if "```" in llm_response:
lines = llm_response.split("```")
for block in lines[1::2]:
code = block.strip()
if code.startswith("json"):
code = code[4:].strip()
try:
data = json.loads(code)
if isinstance(data, dict) and "command" in data:
return data["command"]
except (json.JSONDecodeError, TypeError):
pass
elif code.startswith("bash") or code.startswith("sh"):
code = code.split("\n", 1)[-1].strip()
return code
else:
first_line = code.split("\n")[0].strip()
if first_line:
return first_line
cmd = llm_response.strip().strip("`").strip()
if cmd.startswith("{"):
try:
return json.loads(cmd)["command"]
except Exception:
pass
return cmd
def main():
if not HF_TOKEN:
pass # we can let it fail or use empty key depending on endpoint
client = OpenAI(api_key=HF_TOKEN or "dummy_key", base_url=API_BASE_URL)
TASKS = ["easy", "medium", "hard"]
# Note: openenv evaluation specifically needs exactly 3 things: [START], [STEP] logs, [END]
for task_name in TASKS:
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
try:
with DevopsSandboxEnv(base_url=ENV_URL).sync() as env:
result = env.reset(task_name=task_name)
obs = result.observation
print(f"[START] task={task_name} env={BENCHMARK} model={MODEL_NAME}", flush=True)
messages.append({
"role": "user",
"content": (
f"Here is the initial state of the broken app:\n\n"
f"```\n{obs.stdout}\n```\n\n"
f"Current directory: {obs.current_dir}\n"
f"Score: {obs.grader_score}/1.0\n\n"
f"What bash command should I run first?"
),
})
rewards = []
is_done = False
steps_taken = 0
final_score = getattr(obs, 'grader_score', 0.01)
for turn in range(1, MAX_TURNS + 1):
try:
response = client.chat.completions.create(
model=MODEL_NAME,
messages=messages,
temperature=0.2,
max_tokens=256,
)
llm_text = response.choices[0].message.content or ""
except Exception as e:
err_msg = str(e).replace('"', "'")
break
command = extract_command(llm_text)
if not command:
command = "ls -la /app"
error_msg = "null"
try:
result = env.step(BashAction(command=command))
obs = result.observation
except Exception as e:
obs = env.state # Mock failed obs
error_msg = str(e).replace('\n', ' ')
steps_taken += 1
reward_val = obs.reward if hasattr(obs, 'reward') else getattr(obs, 'grader_score', 0.01)
rewards.append(f"{reward_val:.2f}")
is_done = result.done if hasattr(result, 'done') else getattr(obs, 'done', False)
done_str = "true" if is_done else "false"
action_str = command.replace('\n', ' ; ')
print(f"[STEP] step={steps_taken} action={action_str} reward={reward_val:.2f} done={done_str} error={error_msg}", flush=True)
messages.append({"role": "assistant", "content": llm_text})
messages.append({
"role": "user",
"content": (
f"Command output:\n"
f"stdout:\n```\n{getattr(obs, 'stdout', '')}\n```\n"
f"stderr:\n```\n{getattr(obs, 'stderr', '')}\n```\n"
f"Current score: {getattr(obs, 'grader_score', 0.01)}/1.0\n"
f"Grader feedback: {getattr(obs, 'grader_feedback', '')}\n\n"
f"What command should I run next?"
),
})
final_score = getattr(obs, 'grader_score', 0.01)
if final_score >= 0.99 or getattr(obs, 'done', False) or (hasattr(result, 'done') and result.done):
break
# Clamp final score strictly within (0, 1)
final_score = max(0.01, min(0.99, final_score))
success_str = "true" if final_score >= 0.99 else "false"
rewards_str = ",".join(rewards) if rewards else "0.01"
print(f"[END] success={success_str} steps={steps_taken} score={final_score:.2f} rewards={rewards_str}", flush=True)
except Exception as e:
# Make sure to emit END log even on catastrophic wrapper failures so Hackathon doesn't crash inference.py
print(f"[END] success=false steps=0 score=0.01 rewards=0.01", flush=True)
if __name__ == "__main__":
main()
|