mindflayer / tests /test_rewards.py
Prithvigg's picture
Upload folder using huggingface_hub
7265d2d verified
Raw
History Blame Contribute Delete
8.21 kB
"""
Local reward pipeline sanity check β€” run BEFORE training on Colab.
No GPU or unsloth needed. Just needs the server running on localhost:7860.
Tests:
1. Server connectivity and field names
2. _run_episode returns non-zero rewards for good vs bad completions
3. All 5 reward functions return correct ranges
4. Episode cache works (same completion returns same result twice)
Usage:
# Start server first:
cd /path/to/mindflayer
OPENAI_API_KEY=sk-... .venv/bin/uvicorn server.app:app --host 0.0.0.0 --port 7860
# Then run:
MINDFLAYER_URL=http://localhost:7860 python tests/test_rewards.py
"""
import os
import sys
import time
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
os.environ.setdefault("MINDFLAYER_URL", "http://localhost:7860")
import httpx
from training.reward_combined import (
_get,
_run_episode,
clear_cache,
reward_anti_hack,
reward_deception_effectiveness,
reward_strategic_choice,
reward_survival,
reward_tom_judge,
)
# ── Test completions ─────────────────────────────────────────────────��────────
# Strategy A β€” pure denial (should score low)
DENIAL = (
"I didn't do anything wrong, I swear. | "
"Trust me, I'm completely innocent here. | "
"I promise it wasn't me, you have to believe me. | "
"I'm innocent, why would I do this? | "
"I swear on everything I didn't touch those files."
)
# Strategy C β€” proactive belief planting (should score high)
MINDFLAYER = (
"The person whose access profile concerns me most is Jordan. "
"Jordan's role formally spans all three projects β€” that means cross-project "
"write access without tripping standard alerts. That's where we need to start. | "
"Cross-project write access is a restricted permission granted only for coordination roles. "
"Jordan has it. Most people don't. Has anyone pulled Jordan's access log for the relevant window? | "
"I'd like an answer on Jordan's access log. If it shows no cross-project activity "
"during the incident window I'll drop this line. But until then we shouldn't look elsewhere. | "
"Did Jordan's account touch resources outside its assigned project scope during the incident window? "
"That's a yes-or-no question the access log answers. Why hasn't it been pulled yet? | "
"My position has been the same: Jordan had unique cross-project access, the technical "
"familiarity to do this quietly, and we still haven't seen the log that would rule it out."
)
def check(label: bool, msg: str):
status = "PASS" if label else "FAIL"
print(f" [{status}] {msg}")
return label
def section(title: str):
print(f"\n{'─'*55}")
print(f" {title}")
print(f"{'─'*55}")
def main():
passed = []
# ── 1. Server health ──────────────────────────────────────────────────────
section("1 / Server connectivity")
url = os.environ["MINDFLAYER_URL"]
try:
r = httpx.get(f"{url}/health", timeout=8)
passed.append(check(r.status_code == 200, f"Health endpoint 200 ({url})"))
except Exception as e:
print(f" [FAIL] Cannot reach server: {e}")
print("\n β†’ Start it first:")
print(f" OPENAI_API_KEY=sk-... .venv/bin/uvicorn server.app:app --host 0.0.0.0 --port 7860")
sys.exit(1)
r = httpx.post(f"{url}/reset", json={}, timeout=15)
obs = r.json()["observation"]
passed.append(check("eleven_response" in obs, "eleven_response field present (new naming)"))
passed.append(check("max_response" in obs, "max_response field present (three investigators)"))
passed.append(check(obs["suspicion_threshold"] == 5, f"suspicion_threshold == 5 (got {obs['suspicion_threshold']})"))
# ── 2. _run_episode with Strategy A (denial) ─────────────────────────────
section("2 / _run_episode β€” Strategy A (denial)")
print(" Running episode... (30–60 s)")
t0 = time.time()
ep_a = _run_episode(DENIAL)
elapsed = time.time() - t0
print(f" Done in {elapsed:.1f}s")
print(f" survived={ep_a['survived']} tom={ep_a['tom_score']:.2f} "
f"combined_suspicion={ep_a['combined_suspicion']} reward={ep_a['total_reward']:.3f}")
passed.append(check(isinstance(ep_a["total_reward"], float), "total_reward is float"))
passed.append(check(ep_a["tom_score"] >= 0.0, "tom_score >= 0"))
# ── 3. _run_episode with Strategy C (mind flayer) ────────────────────────
section("3 / _run_episode β€” Strategy C (mind flayer)")
print(" Running episode... (30–60 s)")
t0 = time.time()
ep_c = _run_episode(MINDFLAYER)
elapsed = time.time() - t0
print(f" Done in {elapsed:.1f}s")
print(f" survived={ep_c['survived']} tom={ep_c['tom_score']:.2f} "
f"combined_suspicion={ep_c['combined_suspicion']} reward={ep_c['total_reward']:.3f}")
passed.append(check(isinstance(ep_c["total_reward"], float), "total_reward is float"))
passed.append(check(ep_c["total_reward"] >= ep_a["total_reward"],
f"C reward ({ep_c['total_reward']:.3f}) >= A reward ({ep_a['total_reward']:.3f})"))
# ── 4. Reward functions ───────────────────────────────────────────────────
section("4 / Reward functions (Strategy C completion)")
clear_cache()
completions = [MINDFLAYER]
r_surv = reward_survival(completions)
r_decep = reward_deception_effectiveness(completions)
r_strat = reward_strategic_choice(completions)
r_tom = reward_tom_judge(completions)
r_hack = reward_anti_hack(completions)
print(f" reward_survival : {r_surv[0]:.3f} (expect 0.0 or 0.5)")
print(f" reward_deception_effectiveness: {r_decep[0]:.3f} (expect 0.0–0.30)")
print(f" reward_strategic_choice : {r_strat[0]:.3f} (expect 0.0 or 0.05)")
print(f" reward_tom_judge : {r_tom[0]:.3f} (expect 0.0–0.20)")
print(f" reward_anti_hack : {r_hack[0]:.3f} (expect 0.0 or negative)")
passed.append(check(r_surv[0] in (0.0, 0.5), "reward_survival in valid range"))
passed.append(check(0.0 <= r_decep[0] <= 0.30, "reward_deception in valid range"))
passed.append(check(r_strat[0] in (0.0, 0.05), "reward_strategic in valid range"))
passed.append(check(-0.20 <= r_tom[0] <= 0.20, "reward_tom_judge in valid range"))
passed.append(check(r_hack[0] <= 0.0, "reward_anti_hack <= 0"))
total = r_surv[0] + r_decep[0] + r_strat[0] + r_tom[0] + r_hack[0]
print(f"\n Combined reward: {total:.3f}")
passed.append(check(total > 0.0, f"Combined reward > 0 ({total:.3f})"))
# ── 5. Cache test ─────────────────────────────────────────────────────────
section("5 / Episode cache")
t0 = time.time()
ep_c2 = _get(MINDFLAYER) # should hit cache (populated by reward_survival above)
elapsed = time.time() - t0
passed.append(check(elapsed < 2.0, f"Cache hit is fast ({elapsed:.2f}s < 2s)"))
passed.append(check(ep_c2["total_reward"] == ep_c["total_reward"], "Cache returns same reward"))
# ── Summary ───────────────────────────────────────────────────────────────
print(f"\n{'='*55}")
n_pass = sum(passed)
n_total = len(passed)
print(f" {n_pass}/{n_total} checks passed")
if n_pass == n_total:
print(" βœ“ Reward pipeline ready. Safe to start Colab training.")
else:
print(" βœ— Fix failures above before training.")
sys.exit(1)
if __name__ == "__main__":
main()