Spaces:
Running
Running
File size: 9,016 Bytes
3583511 ec7c2dd 3583511 3210c1c 474eafa 3210c1c 3583511 474eafa ec7c2dd 474eafa 16e8736 3210c1c 474eafa 3583511 561b3cf 3583511 561b3cf 3583511 474eafa 3583511 561b3cf 3583511 ec7c2dd 3583511 ec7c2dd 31940d7 ec7c2dd 59ae86d 474eafa 3583511 3210c1c 3583511 561b3cf 474eafa 3583511 474eafa 3583511 3210c1c 474eafa 3583511 474eafa babbbc8 ec7c2dd babbbc8 31940d7 babbbc8 561b3cf babbbc8 3210c1c babbbc8 3210c1c babbbc8 561b3cf babbbc8 561b3cf 3210c1c babbbc8 3210c1c babbbc8 c04a5c5 561b3cf ec7c2dd 474eafa 3583511 0b0f159 e30d231 0b0f159 e30d231 0b0f159 e30d231 474eafa 3210c1c e30d231 3210c1c 98bf903 474eafa cd2bb35 561b3cf 474eafa 3583511 474eafa 3210c1c 474eafa 3583511 ec7c2dd 474eafa 3583511 | 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 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | """
Baseline inference script for Code Security Review OpenEnv.
Compliant with mandatory STDOUT format: [START], [STEP], [END].
Required environment variables (injected by grader):
API_BASE_URL β LiteLLM proxy endpoint
API_KEY β LiteLLM proxy key
ENV_URL β Running environment URL (default: http://localhost:7860)
MODEL_NAME β Model identifier (default: gpt-4o-mini)
"""
import os
import json
import re
import requests
from typing import List, Optional
from openai import OpenAI
# ββ Config (NO load_dotenv β grader injects env vars directly) ββββββββββββββββ
ENV_URL = os.environ.get("ENV_URL", "http://localhost:7860")
MODEL_NAME = os.environ.get("MODEL_NAME", "gpt-4o-mini")
BENCHMARK = "code-security-review"
SYSTEM_PROMPT = """You are a senior security-focused code reviewer.
You are interacting with a multi-step environment. At first, the code snippet will be HIDDEN.
To request the file contents, you must output EXACTLY this JSON (no other text):
{"request_file": true}
Once you have requested the file and read the code snippet, carefully analyse it for bugs and security issues.
To submit your final review, respond with ONLY a valid JSON object matching this schema (no code blocks, no prose):
{
"bug_identified": true or false,
"bug_location": "exact location (function name, line description, variable, expression)",
"bug_type": "off-by-one | logic-error | security-vulnerability | none",
"bug_description": "detailed explanation of why this is a bug and the impact",
"severity": "none | low | medium | high | critical",
"suggested_fix": "description of fix (do NOT include code blocks inside this string)"
}
IMPORTANT: Your entire response must be parseable JSON. Do not wrap in markdown fences. Do not add any text outside the JSON object."""
# ββ Logging Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def log_start(task: str, env: str, model: str) -> None:
print(f"[START] task={task} env={env} model={model}", flush=True)
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None:
error_val = error if error else "null"
done_val = str(done).lower()
print(
f"[STEP] step={step} action={action} reward={reward:.2f} done={done_val} error={error_val}",
flush=True,
)
def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
rewards_str = ",".join(f"{r:.2f}" for r in rewards)
print(f"[END] success={str(success).lower()} steps={steps} score={score:.3f} rewards={rewards_str}", flush=True)
# ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def env_post(path: str, data: Optional[dict] = None, params: Optional[dict] = None) -> dict:
url = f"{ENV_URL}{path}"
resp = requests.post(url, json=data or {}, params=params or {}, timeout=30)
resp.raise_for_status()
return resp.json()
def parse_json_from_llm(text: str) -> dict:
"""Robustly extract JSON from LLM output."""
text = text.strip()
text = re.sub(r"```(?:json)?\s*", "", text)
text = re.sub(r"```", "", text)
candidates = re.findall(r"(\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\})", text, re.DOTALL)
for candidate in reversed(candidates):
try:
parsed = json.loads(candidate)
if isinstance(parsed, dict):
return parsed
except Exception:
continue
try:
return json.loads(text)
except Exception:
return {}
def build_prompt(obs: dict) -> str:
lines = [
f"Language: {obs['language']}",
f"Context: {obs.get('context', 'No context provided')}",
f"PR Title: {obs.get('pr_title', 'No PR title')}",
f"File Path: {obs.get('file_path', 'unknown')}",
"",
f"```{obs['language']}",
obs["code_snippet"],
"```",
]
return "\n".join(lines)
# ββ Task runner βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def run_task(task_id: str, task_num: int, client: OpenAI) -> dict:
cumulative_reward = 0.0
step_num = 0
done = False
all_rewards = []
success = False
try:
log_start(task=task_id, env=BENCHMARK, model=MODEL_NAME)
reset_resp = env_post("/reset", params={"task_id": task_id})
obs = reset_resp["observation"]
max_steps = 2
error = None
messages = []
while not done and step_num < max_steps:
step_num += 1
prompt = build_prompt(obs)
action_dict = {}
# ββ LLM call (must go through grader proxy) ββββββββββββββββββββββ
if not messages:
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
messages.append({"role": "user", "content": prompt})
response = client.chat.completions.create(
model=MODEL_NAME,
messages=messages,
temperature=0.1,
max_tokens=600,
stream=False,
)
raw = response.choices[0].message.content
messages.append({"role": "assistant", "content": raw})
action_dict = parse_json_from_llm(raw)
action_str = json.dumps(action_dict)
# ββ Step env βββββββββββββββββββββββββββββββββββββββββββββββββββββ
step_resp = env_post("/step", data=action_dict)
reward = step_resp["reward"]
done = step_resp["done"]
obs = step_resp.get("observation")
all_rewards.append(reward)
cumulative_reward += reward
log_step(step=step_num, action=action_str, reward=reward, done=done, error=None)
success = cumulative_reward >= 0.8
except Exception as exc:
print(f"[ERROR] task={task_id} exception: {exc}", flush=True)
finally:
clamped_score = round(min(0.99, max(0.01, cumulative_reward)), 3)
log_end(success=success, steps=step_num, score=clamped_score, rewards=all_rewards)
return {
"task_num": task_num,
"task_id": task_id,
"score": cumulative_reward,
"success": success,
}
# ββ Main ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def main():
# Read proxy config exactly as the sample inference.py specifies
api_base = os.getenv("API_BASE_URL", "https://api.openai.com/v1").strip()
api_key = os.getenv("HF_TOKEN", "").strip() or os.getenv("API_KEY", "").strip()
# Validate β crash with a clear message if the grader didn't inject a key
if not api_base:
raise RuntimeError("API_BASE_URL is empty or not set")
if not api_key:
raise RuntimeError("Neither HF_TOKEN nor API_KEY is set")
# Ensure base_url ends with / (httpx requires it)
if not api_base.endswith("/"):
api_base += "/"
print(f"[INFO] API_BASE_URL = {api_base}", flush=True)
print(f"[INFO] MODEL_NAME = {MODEL_NAME}", flush=True)
print(f"[INFO] ENV_URL = {ENV_URL}", flush=True)
# Clear any conflicting OPENAI_* env vars so the openai library
# doesn't silently override our base_url / api_key
os.environ.pop("OPENAI_BASE_URL", None)
os.environ.pop("OPENAI_API_KEY", None)
os.environ.pop("OPENAI_API_BASE", None)
# Initialize OpenAI client pointing at grader's LiteLLM proxy
client = OpenAI(base_url=api_base, api_key=api_key)
print(f"[INFO] OpenAI client initialized successfully", flush=True)
TASK_FILTER = os.environ.get("TASK")
all_tasks = [
("python-off-by-one", 1, "easy"),
("js-idor-auth", 2, "medium"),
("python-pickle-deserialization", 3, "hard"),
]
if TASK_FILTER:
tasks = [t for t in all_tasks if t[2] == TASK_FILTER]
else:
tasks = all_tasks
results = []
for task_id, task_num, _ in tasks:
r = run_task(task_id, task_num, client=client)
results.append(r)
if results:
avg = round(sum(r["score"] for r in results) / len(results), 3)
successes = sum(1 for r in results if r.get("success"))
print(f"\n[SUMMARY] avg_reward={avg} tasks_passed={successes}/{len(results)}", flush=True)
if __name__ == "__main__":
main()
|