vajeeda's picture
Phase 7 implemented
0e4f105
Raw
History Blame Contribute Delete
6.96 kB
#!/usr/bin/env python3
import json
import sys
from pathlib import Path
import numpy as np
from dotenv import load_dotenv
from rich.console import Console
from rich.table import Table
from rich import box
load_dotenv()
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from viral_script_engine.agents.baseline_arbitrator import BaselineArbitratorAgent
from viral_script_engine.environment.env import ViralScriptEnv
console = Console()
BASE_DIR = Path(__file__).parent.parent
LOGS_DIR = BASE_DIR / "logs"
LOGS_DIR.mkdir(exist_ok=True)
_SCHEDULE = (
[(i, "easy") for i in range(1, 9)]
+ [(i, "medium") for i in range(9, 17)]
+ [(i, "hard") for i in range(17, 21)]
)
_REWARD_KEYS = ["r1_hook_strength", "r2_coherence", "r3_cultural_alignment",
"r4_debate_resolution", "r5_defender_preservation"]
def _make_env(difficulty: str) -> ViralScriptEnv:
return ViralScriptEnv(
scripts_path=str(BASE_DIR / "data" / "test_scripts" / "scripts.json"),
cultural_kb_path=str(BASE_DIR / "data" / "cultural_kb.json"),
max_steps=5,
difficulty=difficulty,
)
def run_episode(ep_num: int, difficulty: str, agent: BaselineArbitratorAgent) -> dict:
env = _make_env(difficulty)
obs, _ = env.reset()
episode_id = obs["episode_id"]
script_id = "unknown"
state = env.state()
original_script = state.get("original_script", "")
steps_log = []
total_reward = 0.0
for _ in range(env.max_steps):
action = agent.act(obs)
obs, reward, terminated, truncated, info = env.step(action)
rc = info["reward_components"]
anti_log = info.get("anti_gaming_log", {})
step_entry = {
"r1": rc.get("r1_hook_strength"),
"r2": rc.get("r2_coherence"),
"r3": rc.get("r3_cultural_alignment"),
"r4": rc.get("r4_debate_resolution"),
"r5": rc.get("r5_defender_preservation"),
"process_reward": rc.get("process_reward"), # Phase 7 — expected ~0 for untrained
"total": reward,
"anti_gaming_triggered": anti_log.get("triggered", False),
"penalty": anti_log.get("penalty_applied", 0.0),
}
steps_log.append(step_entry)
total_reward = reward
if terminated or truncated:
break
final_state = env.state()
final_script = final_state.get("current_script", "")
return {
"episode_num": ep_num,
"episode_id": episode_id,
"difficulty": difficulty,
"script_id": script_id,
"steps": steps_log,
"total_reward": total_reward,
"anti_gaming_logs": final_state.get("anti_gaming_logs", []),
"original_script": original_script,
"final_script": final_script,
}
def main():
agent = BaselineArbitratorAgent()
all_episodes = []
for ep_num, difficulty in _SCHEDULE:
console.print(f"[dim]Episode {ep_num:02d}/20 ({difficulty})...[/dim]")
try:
result = run_episode(ep_num, difficulty, agent)
all_episodes.append(result)
console.print(
f" -> total_reward={result['total_reward']:.3f} "
f"steps={len(result['steps'])}"
)
except Exception as e:
console.print(f" [red]ERROR episode {ep_num}: {e}[/red]")
all_episodes.append({
"episode_num": ep_num,
"episode_id": "",
"difficulty": difficulty,
"script_id": "error",
"steps": [],
"total_reward": 0.0,
"anti_gaming_logs": [],
"original_script": "",
"final_script": "",
"error": str(e),
})
results_path = LOGS_DIR / "baseline_results_v2.json"
with open(results_path, "w", encoding="utf-8") as f:
json.dump(all_episodes, f, indent=2, default=str)
_save_plots(all_episodes)
_print_summary(all_episodes)
mean_total = float(np.mean([e["total_reward"] for e in all_episodes]))
console.print(
f"\n[bold green]PHASE 2 GATE: PASS — Baseline curves saved. "
f"Pre-training mean total reward: {mean_total:.2f}[/bold green]"
)
def _collect_reward_series(episodes: list, key: str):
series = []
for ep in episodes:
vals = [s.get(key) for s in ep.get("steps", []) if s.get(key) is not None]
series.append(vals[-1] if vals else 0.0)
return series
def _save_plots(episodes: list):
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
labels = {
"r1": "R1 Hook Strength",
"r2": "R2 Coherence",
"r3": "R3 Cultural Alignment",
"r4": "R4 Debate Resolution",
"r5": "R5 Defender Preservation",
"total": "Total Reward",
}
keys = list(labels.keys())
ep_nums = [e["episode_num"] for e in episodes]
fig, axes = plt.subplots(2, 3, figsize=(14, 8), dpi=150)
fig.suptitle(
"Baseline (Untrained) Arbitrator — Pre-Training Reward Curves",
fontsize=13,
)
for idx, key in enumerate(keys):
ax = axes[idx // 3][idx % 3]
series = _collect_reward_series(episodes, key) if key != "total" else [e["total_reward"] for e in episodes]
ax.plot(ep_nums, series, marker="o", linewidth=1.5, markersize=4)
ax.set_title(labels[key], fontsize=10)
ax.set_xlabel("Episode", fontsize=8)
ax.set_ylabel("Reward", fontsize=8)
ax.set_ylim(0, 1)
ax.set_xlim(min(ep_nums) - 0.5, max(ep_nums) + 0.5)
ax.tick_params(labelsize=7)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plot_path = LOGS_DIR / "baseline_reward_curves.png"
plt.savefig(str(plot_path), dpi=150)
plt.close()
console.print(f"[dim]Curves saved -> {plot_path}[/dim]")
def _print_summary(episodes: list):
table = Table(title="Baseline Results — Mean +/- Std (20 episodes)", box=box.SIMPLE_HEAD)
table.add_column("Reward", style="cyan", min_width=28)
table.add_column("Mean", min_width=8)
table.add_column("Std", min_width=8)
table.add_column("Min", min_width=8)
table.add_column("Max", min_width=8)
label_map = {
"r1": "R1 Hook Strength",
"r2": "R2 Coherence",
"r3": "R3 Cultural Alignment",
"r4": "R4 Debate Resolution",
"r5": "R5 Defender Preservation",
"total": "Total Reward",
}
for key, label in label_map.items():
if key == "total":
vals = [e["total_reward"] for e in episodes]
else:
vals = _collect_reward_series(episodes, key)
arr = np.array(vals, dtype=float)
table.add_row(
label,
f"{arr.mean():.3f}",
f"{arr.std():.3f}",
f"{arr.min():.3f}",
f"{arr.max():.3f}",
)
console.print(table)
if __name__ == "__main__":
main()