Negoti_Arena / server /app.py
abrar6024's picture
Fix merge conflict markers
36cd62b
Raw
History Blame Contribute Delete
10.2 kB
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
# =====================================================
@app.get("/")
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
# =====================================================
@app.get("/blog", response_class=HTMLResponse)
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
# =====================================================
@app.get("/api/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
# =====================================================
@app.get("/api/training_results")
def get_training_results():
return JSONResponse(content=DASHBOARD_DATA["training"])
# =====================================================
# PERFORMANCE
# =====================================================
@app.get("/api/performance")
def get_performance():
return JSONResponse(content=DASHBOARD_DATA["performance"])
# =====================================================
# DATASET INFO
# =====================================================
@app.get("/api/dataset")
def get_dataset():
return JSONResponse(content=DASHBOARD_DATA["dataset"])
# =====================================================
# REWARD COMPONENTS
# =====================================================
@app.get("/api/reward_components")
def get_reward_components():
return JSONResponse(content=DASHBOARD_DATA["reward_components"])
# =====================================================
# REWARD HACKING
# =====================================================
@app.get("/api/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
)