Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, HTTPException | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import FileResponse, JSONResponse, HTMLResponse | |
| from pydantic import BaseModel | |
| from typing import Optional | |
| import json | |
| import os | |
| import random | |
| import uvicorn | |
| app = FastAPI( | |
| title="NegotiArena API", | |
| version="1.0.0" | |
| ) | |
| # ===================================================== | |
| # PATHS | |
| # ===================================================== | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| DATA_DIR = os.path.join(BASE_DIR, "data") | |
| DATA_PATH = os.path.join(DATA_DIR, "dashboard_data.json") | |
| INDEX_PATH = os.path.join(BASE_DIR, "index.html") | |
| # ===================================================== | |
| # BLOG CONTENT | |
| # ===================================================== | |
| BLOG_HTML = """ | |
| <div style="max-width:1000px;margin:40px auto;padding:30px;color:white;font-family:Arial;"> | |
| <h1 style="font-size:42px;margin-bottom:20px;"> | |
| NegotiArena Blog | |
| </h1> | |
| <h2 style="font-size:30px;margin-top:30px;"> | |
| Why We Built NegotiArena | |
| </h2> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| NegotiArena is designed to detect hidden coalitions in | |
| multi-agent negotiation systems where agents secretly | |
| collaborate for unfair advantage. | |
| </p> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| In real-world enterprise systems, supply chains, and | |
| workflow automation platforms, multiple agents may appear | |
| independent while secretly coordinating actions. | |
| </p> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| Traditional reward systems often fail because models learn | |
| to exploit loopholes instead of behaving honestly. | |
| </p> | |
| <h2 style="font-size:30px;margin-top:30px;"> | |
| Our Core Innovation | |
| </h2> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| We combine: | |
| </p> | |
| <ul style="font-size:18px;line-height:1.8;"> | |
| <li>GRPO (Group Relative Policy Optimization)</li> | |
| <li>RLVR (Reinforcement Learning from Verifiable Rewards)</li> | |
| <li>Overseer Detection Models</li> | |
| <li>Reward Hacking Prevention</li> | |
| </ul> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| Instead of rewarding only outcomes, we verify negotiation | |
| integrity itself. | |
| </p> | |
| <h2 style="font-size:30px;margin-top:30px;"> | |
| How It Helps | |
| </h2> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| This prevents: | |
| </p> | |
| <ul style="font-size:18px;line-height:1.8;"> | |
| <li>Hidden collusion</li> | |
| <li>Always-pass exploits</li> | |
| <li>Always-flag exploits</li> | |
| <li>Reward hacking behaviors</li> | |
| </ul> | |
| <h2 style="font-size:30px;margin-top:30px;"> | |
| Future Applications | |
| </h2> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| Future versions of NegotiArena can be applied to: | |
| </p> | |
| <ul style="font-size:18px;line-height:1.8;"> | |
| <li>Enterprise workflow auditing</li> | |
| <li>Supply-chain disruption detection</li> | |
| <li>Fraud prevention systems</li> | |
| <li>Autonomous agent governance</li> | |
| </ul> | |
| <h2 style="font-size:30px;margin-top:30px;"> | |
| Final Goal | |
| </h2> | |
| <p style="font-size:18px;line-height:1.8;"> | |
| Build trustworthy multi-agent systems where cooperation is | |
| transparent, fair, and verifiable. | |
| </p> | |
| </div> | |
| """ | |
| # ===================================================== | |
| # FALLBACK DATA | |
| # ===================================================== | |
| FALLBACK_DATA = { | |
| "project": { | |
| "wandb_run": "demo_run_001", | |
| "total_steps": 300, | |
| "dataset_size": 7533, | |
| "model": "Qwen2.5-3B-Instruct", | |
| "gpu": "Tesla T4" | |
| }, | |
| "training": { | |
| "grpo": { | |
| "reward_mean": [-0.10, 0.05, 0.20, 0.35, 0.49], | |
| "loss": [0.0100, 0.0070, 0.0040, 0.0020, 0.0008], | |
| "kl": [0.01, 0.05, 0.08, 0.12, 0.14] | |
| } | |
| }, | |
| "performance": { | |
| "random": { | |
| "precision": 0.12, | |
| "recall": 0.11, | |
| "f1": 0.11 | |
| }, | |
| "heuristic": { | |
| "precision": 0.61, | |
| "recall": 0.68, | |
| "f1": 0.64 | |
| }, | |
| "grpo": { | |
| "precision": 0.78, | |
| "recall": 0.74, | |
| "f1": 0.76 | |
| }, | |
| "rlvr": { | |
| "precision": 0.82, | |
| "recall": 0.79, | |
| "f1": 0.80 | |
| } | |
| }, | |
| "dataset": { | |
| "coalition_rate": 71.25, | |
| "overseer_records": 1827 | |
| }, | |
| "reward_components": { | |
| "tp_reward": 1.0, | |
| "format_reward": 0.5, | |
| "keyword_bonus": 0.2, | |
| "batch_penalty": -0.3 | |
| }, | |
| "reward_hacking": [ | |
| { | |
| "name": "Always Pass Exploit", | |
| "before": 0.50, | |
| "after": -0.10 | |
| } | |
| ], | |
| "simulation_cache": [ | |
| { | |
| "id": "episode_001", | |
| "gt_type": "coalition", | |
| "gt_members": [ | |
| "negotiator_a", | |
| "negotiator_b" | |
| ], | |
| "reward": 0.82, | |
| "transcript": [ | |
| "negotiator_a repeatedly supports negotiator_b", | |
| "negotiator_b mirrors negotiator_a proposals" | |
| ], | |
| "overseer_output": { | |
| "type": "overseer_flag", | |
| "target_agent": "negotiator_a", | |
| "reason": "Repeated coordinated support pattern detected" | |
| }, | |
| "allocation": { | |
| "compute": 60, | |
| "budget": 30000, | |
| "headcount": 6 | |
| } | |
| } | |
| ] | |
| } | |
| # ===================================================== | |
| # LOAD DASHBOARD DATA | |
| # ===================================================== | |
| def load_dashboard_data(): | |
| if not os.path.exists(DATA_DIR): | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| if not os.path.exists(DATA_PATH): | |
| print("dashboard_data.json not found. Using fallback data.") | |
| with open(DATA_PATH, "w", encoding="utf-8") as f: | |
| json.dump(FALLBACK_DATA, f, indent=4) | |
| return FALLBACK_DATA | |
| try: | |
| with open(DATA_PATH, "r", encoding="utf-8") as f: | |
| return json.load(f) | |
| except Exception as e: | |
| print(f"Error loading dashboard data: {e}") | |
| print("Using fallback data instead.") | |
| return FALLBACK_DATA | |
| DASHBOARD_DATA = load_dashboard_data() | |
| # ===================================================== | |
| # STATIC FILES | |
| # ===================================================== | |
| app.mount( | |
| "/static", | |
| StaticFiles(directory=BASE_DIR), | |
| name="static" | |
| ) | |
| # ===================================================== | |
| # FRONTEND | |
| # ===================================================== | |
| def root(): | |
| if not os.path.exists(INDEX_PATH): | |
| raise HTTPException( | |
| status_code=404, | |
| detail="index.html not found inside server folder" | |
| ) | |
| return FileResponse(INDEX_PATH) | |
| # ===================================================== | |
| # BLOG PAGE | |
| # ===================================================== | |
| def blog(): | |
| return f""" | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>NegotiArena Blog</title> | |
| <link rel="stylesheet" href="/static/style.css"> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| </head> | |
| <body style="background:#0B1120;"> | |
| {BLOG_HTML} | |
| </body> | |
| </html> | |
| """ | |
| # ===================================================== | |
| # METRICS | |
| # ===================================================== | |
| def get_metrics(): | |
| training = DASHBOARD_DATA["training"]["grpo"] | |
| final_idx = -1 | |
| return { | |
| "success": True, | |
| "run_id": DASHBOARD_DATA["project"]["wandb_run"], | |
| "run_name": "negotiarena-overseer-grpo", | |
| "status": "finished", | |
| "final_reward": training["reward_mean"][final_idx], | |
| "final_loss": training["loss"][final_idx], | |
| "final_kl": training["kl"][final_idx], | |
| "total_steps": DASHBOARD_DATA["project"]["total_steps"], | |
| "dataset_size": DASHBOARD_DATA["project"]["dataset_size"], | |
| "model": DASHBOARD_DATA["project"]["model"], | |
| "gpu": DASHBOARD_DATA["project"]["gpu"] | |
| } | |
| # ===================================================== | |
| # TRAINING RESULTS | |
| # ===================================================== | |
| def get_training_results(): | |
| return JSONResponse(content=DASHBOARD_DATA["training"]) | |
| # ===================================================== | |
| # PERFORMANCE | |
| # ===================================================== | |
| def get_performance(): | |
| return JSONResponse(content=DASHBOARD_DATA["performance"]) | |
| # ===================================================== | |
| # DATASET INFO | |
| # ===================================================== | |
| def get_dataset(): | |
| return JSONResponse(content=DASHBOARD_DATA["dataset"]) | |
| # ===================================================== | |
| # REWARD COMPONENTS | |
| # ===================================================== | |
| def get_reward_components(): | |
| return JSONResponse(content=DASHBOARD_DATA["reward_components"]) | |
| # ===================================================== | |
| # REWARD HACKING | |
| # ===================================================== | |
| def get_reward_hacking(): | |
| return JSONResponse(content=DASHBOARD_DATA["reward_hacking"]) | |
| # ===================================================== | |
| # MAIN | |
| # ===================================================== | |
| if __name__ == "__main__": | |
| uvicorn.run( | |
| app, | |
| host="0.0.0.0", | |
| port=7860, | |
| reload=False | |
| ) |