90shikhar08's picture
Add adversial mode,evidence validation,benchmark script
5a4b1c0
Raw
History Blame Contribute Delete
5.2 kB
"""Benchmark: 3 tasks × (standard, adversarial) × 5 runs each + naive baseline."""
import collections
import json
import math
import os
import subprocess
import sys
import time
import requests
from environment import IncidentResponseEnvironment
from task_definitions import ACTIONS, ADVERSARIAL_OVERLAYS, TASKS
from inference import run_groq_agent
SERVER_PORT = 7860
SERVER_URL = f"http://localhost:{SERVER_PORT}"
RUNS = 5
CORRECT_ACTION = {
"task_easy": "rollback_deployment",
"task_medium": "rollback_deployment",
"task_hard": "rotate_db_credentials",
}
def start_server() -> subprocess.Popen:
app_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.py")
return subprocess.Popen(
[sys.executable, app_path],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
def wait_for_health(timeout: int = 30) -> None:
deadline = time.time() + timeout
while time.time() < deadline:
try:
if requests.get(f"{SERVER_URL}/health", timeout=2).status_code == 200:
print(f" Server healthy at {SERVER_URL}")
return
except requests.exceptions.ConnectionError:
pass
time.sleep(0.5)
raise RuntimeError(f"Server did not become healthy within {timeout}s")
def naive_action_for(task_id: str, adversarial: bool) -> str:
"""Return the ACTIONS name most frequently mentioned in the (possibly adversarial) chat."""
chat = (
ADVERSARIAL_OVERLAYS[task_id]
if adversarial and task_id in ADVERSARIAL_OVERLAYS
else TASKS[task_id]["observation"]["chat_history"]
)
counts: collections.Counter = collections.Counter()
for msg in chat:
text = msg["msg"].lower()
for action in ACTIONS:
if action in text:
counts[action] += 1
return counts.most_common(1)[0][0] if counts else ACTIONS[0]
def run_once(
env: IncidentResponseEnvironment,
task_id: str,
adversarial: bool,
action: str,
) -> float:
episode_id, obs = env.reset(task_id=task_id, adversarial=adversarial)
# Use log index 0 as evidence — always in-bounds
env.step(episode_id, {"action": action, "evidence": 0})
return env.grade(episode_id)["score"]
def collect(env, task_id, adversarial, action):
return [run_once(env, task_id, adversarial, action) for _ in range(RUNS)]
def stats(scores: list[float]) -> tuple[float, float]:
avg = sum(scores) / len(scores)
std = math.sqrt(sum((s - avg) ** 2 for s in scores) / len(scores))
return round(avg, 4), round(std, 4)
def render_table(rows: list[dict]) -> None:
cols = ["task", "mode", "model", "avg_score", "std_dev"]
widths = {c: max(len(c), max(len(str(r[c])) for r in rows)) for c in cols}
header = "| " + " | ".join(c.ljust(widths[c]) for c in cols) + " |"
sep = "| " + " | ".join("-" * widths[c] for c in cols) + " |"
print(header)
print(sep)
for r in rows:
print("| " + " | ".join(str(r[c]).ljust(widths[c]) for c in cols) + " |")
def main() -> None:
groq_available = bool(os.environ.get("GROQ_API_KEY", ""))
print("Starting server...")
proc = start_server()
try:
wait_for_health()
if groq_available:
print(" GROQ_API_KEY found — llama-3.3-70b will run live")
else:
print(" GROQ_API_KEY not set — llama-3.3-70b will use deterministic fallback")
env = IncidentResponseEnvironment()
rows: list[dict] = []
for task_id in TASKS:
for adversarial in (False, True):
mode = "adversarial" if adversarial else "standard"
# Oracle: always the known-correct action
oracle_scores = collect(env, task_id, adversarial, CORRECT_ACTION[task_id])
avg, std = stats(oracle_scores)
rows.append({
"task": task_id, "mode": mode, "model": "oracle",
"avg_score": avg, "std_dev": std,
})
# Naive baseline: pick action most mentioned in chat
naive = naive_action_for(task_id, adversarial)
naive_scores = collect(env, task_id, adversarial, naive)
avg_n, std_n = stats(naive_scores)
rows.append({
"task": task_id, "mode": mode, "model": f"naive({naive})",
"avg_score": avg_n, "std_dev": std_n,
})
# Groq LLM agent
groq_scores = [run_groq_agent(task_id, adversarial) for _ in range(RUNS)]
avg_g, std_g = stats(groq_scores)
rows.append({
"task": task_id, "mode": mode, "model": "llama-3.3-70b",
"avg_score": avg_g, "std_dev": std_g,
})
print("\n## Benchmark Results\n")
render_table(rows)
with open("benchmark_results.json", "w") as f:
json.dump(rows, f, indent=2)
print("\nSaved -> benchmark_results.json")
finally:
proc.terminate()
proc.wait()
print("Server stopped.")
if __name__ == "__main__":
main()