Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| import json | |
| import time | |
| import glob | |
| import importlib.util | |
| import random | |
| import asyncio | |
| from typing import Optional, List | |
| from openai import AsyncOpenAI | |
| import uvicorn | |
| from fastapi import FastAPI, Request, HTTPException, WebSocket, WebSocketDisconnect | |
| from fastapi.responses import HTMLResponse, RedirectResponse | |
| from pydantic import BaseModel | |
| from openenv.core.env_server import create_app | |
| # ============================================================================== | |
| # PHASE 1: NATIVE ENGINE BRIDGE & DYNAMIC LOADING | |
| # ============================================================================== | |
| _CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| _ROOT_DIR = os.path.abspath(os.path.join(_CURRENT_DIR, "..")) | |
| if _ROOT_DIR not in sys.path: | |
| sys.path.insert(0, _ROOT_DIR) | |
| if _CURRENT_DIR not in sys.path: | |
| sys.path.insert(0, _CURRENT_DIR) | |
| # Always define this as a safe global so /dashboard/state never throws NameError | |
| # even when NATIVE_VERIFIED is False (C++ binary missing). | |
| active_env_instance = None | |
| try: | |
| from server.fin_auditor_environment import FinAuditorEnvironment, hft_auditor | |
| from models import AuditorAction, AuditorObservation | |
| from tasks import task1_easy, task2_medium, task3_hard | |
| # Graders — used by the /grader evaluation endpoint | |
| from graders.grader_detection import EasyDetectionGrader | |
| from graders.grader_classification import MediumClassificationGrader | |
| from graders.grader_fix import HardFixGrader | |
| # Instantiate graders once at module level so they accumulate state | |
| _grader_easy = EasyDetectionGrader() | |
| _grader_medium = MediumClassificationGrader() | |
| _grader_hard = HardFixGrader() | |
| HAS_ENV = True | |
| NATIVE_VERIFIED = hft_auditor is not None | |
| hft_mod = hft_auditor | |
| except ImportError as e: | |
| HAS_ENV = False | |
| NATIVE_VERIFIED = False | |
| hft_mod = None | |
| _grader_easy = None | |
| _grader_medium = None | |
| _grader_hard = None | |
| print(f"\n[CRITICAL WARNING] Could not import dependencies. Running in fallback UI mode.") | |
| print(f"Exact Error: {e}\n") | |
| # ============================================================================== | |
| # PHASE 2: SYSTEM STATE & AUTHORITY TRACKING | |
| # ============================================================================== | |
| if HAS_ENV and NATIVE_VERIFIED: | |
| # ── Dashboard singleton ──────────────────────────────────────────────────── | |
| # Used ONLY by /dashboard/* endpoints and /ws/telemetry for live telemetry. | |
| # NEVER passed to create_app — OpenEnv gets a factory that produces isolated | |
| # instances per session so close() cannot corrupt the dashboard engine. | |
| active_env_instance = FinAuditorEnvironment() | |
| # Pre-load the first batch so the dashboard has real data immediately. | |
| # (reset() calls generate_batch internally, so just call reset here.) | |
| active_env_instance.reset() | |
| # ── OpenEnv factory ──────────────────────────────────────────────────────── | |
| # CRITICAL: OpenEnv's WebSocket server creates ONE env per session via | |
| # env_factory() and then sends reset + step messages to the SAME instance. | |
| # This means reset() IS called before step(), so the C++ engine has data. | |
| # For HTTP mode (stateless), each request gets its own env — step() is called | |
| # on a cold engine, but our __init__ initialises counters to 0 so it won't | |
| # crash; it will just return the floor reward of 0.01 (acceptable for Phase 2). | |
| def env_factory() -> FinAuditorEnvironment: | |
| """Create a fresh, self-contained FinAuditorEnvironment per OpenEnv session.""" | |
| return FinAuditorEnvironment() | |
| # NOTE: create_app() has no `tasks=` parameter in openenv-core >= 0.2.x. | |
| # Task routing (easy/medium/hard difficulty) is handled inside reset() via | |
| # the task_id kwarg that Phase 2 injects into the reset message body. | |
| app = create_app( | |
| env_factory, | |
| AuditorAction, | |
| AuditorObservation, | |
| ) | |
| async def health_check(): | |
| """Liveness probe for HF Space / Docker.""" | |
| return {"status": "healthy", "engine": "NATIVE_ACTIVE" if NATIVE_VERIFIED else "MOCK"} | |
| else: | |
| # Fallback for local development without the C++ binary | |
| app = FastAPI(title="PayGorn (MOCK MODE)") | |
| async def mock_reset(): return {"reward": 0.01} | |
| async def mock_step(action: dict): return {"reward": 0.5, "done": False, "step_count": 0} | |
| # Initialize metrics for the dashboard latency middleware | |
| app_metrics = {"last_step_latency_us": 0.0} | |
| # ── Auto-bootstrap on startup ──────────────────────────────────────────────── | |
| async def auto_bootstrap(): | |
| """Auto-authenticate with HF_TOKEN and initialize engine on boot.""" | |
| token = os.getenv("HF_TOKEN", "") | |
| if token: | |
| try: | |
| from openai import AsyncOpenAI | |
| client = AsyncOpenAI(base_url="https://router.huggingface.co/v1", api_key=token, max_retries=2) | |
| try: | |
| response = await client.models.list() | |
| model_list = [m.id for m in response.data] | |
| except Exception: | |
| model_list = ["meta-llama/Meta-Llama-3-8B-Instruct"] | |
| llm_session["api_key"] = token | |
| llm_session["base_url"] = "https://router.huggingface.co/v1" | |
| llm_session["available_models"] = model_list | |
| if model_list: | |
| llm_session["model_name"] = model_list[0] | |
| system_health["key_validated"] = True | |
| system_health["model_detected"] = len(model_list) > 0 | |
| system_health["connected"] = True | |
| print(f"[BOOT] Auto-authenticated with HF_TOKEN. {len(model_list)} models discovered.") | |
| except Exception as e: | |
| print(f"[BOOT] Auto-auth failed: {e}") | |
| else: | |
| print("[BOOT] No HF_TOKEN found. Manual authentication required.") | |
| async def capture_step_latency(request: Request, call_next): | |
| if request.url.path in ("/step", "/dashboard/step"): | |
| start_ns = time.perf_counter_ns() | |
| response = await call_next(request) | |
| app_metrics["last_step_latency_us"] = (time.perf_counter_ns() - start_ns) / 1000.0 | |
| return response | |
| return await call_next(request) | |
| async def root_redirect(): | |
| return RedirectResponse(url="/web") | |
| system_health = { | |
| "so_found": NATIVE_VERIFIED, | |
| "key_validated": False, | |
| "model_detected": False, | |
| "connected": False, | |
| } | |
| llm_session = { | |
| "base_url": "https://router.huggingface.co/v1", | |
| "api_key": os.getenv("HF_TOKEN", ""), | |
| "model_name": "", | |
| "available_models": [] | |
| } | |
| class LLMConfig(BaseModel): | |
| api_key: str = "" | |
| model_name: Optional[str] = None | |
| base_url: Optional[str] = None | |
| class DifficultyConfig(BaseModel): | |
| level: str | |
| class ActionRequest(BaseModel): | |
| action_type: str | |
| async def execute_llm_step(api_key: str, base_url: str, model_name: str, batch_size: int = 40) -> list[int]: | |
| client = AsyncOpenAI(api_key=api_key, base_url=base_url, max_retries=1) | |
| prompt = ( | |
| "You are a High-Frequency Trading Risk Compliance Auditor.\n" | |
| f"You are processing a microsecond batch of {batch_size} trades.\n" | |
| "Rule 1: A False Negative (missing fraud) is catastrophic.\n" | |
| "Rule 2: A False Positive (flagging a valid trade) is a minor penalty.\n" | |
| f"Based on standard HFT anomaly distributions, output exactly {batch_size} binary decisions.\n" | |
| "0 = VALID, 1 = ANOMALY.\n" | |
| "Respond ONLY in valid JSON format using this exact schema: {\"decisions\": [int, int, ...]}" | |
| ) | |
| try: | |
| response = await client.chat.completions.create( | |
| model=model_name, | |
| messages=[ | |
| {"role": "system", "content": prompt}, | |
| {"role": "user", "content": "Analyze the telemetry stream and output the decision matrix."} | |
| ], | |
| response_format={"type": "json_object"}, | |
| temperature=0.2, | |
| max_tokens=2000 | |
| ) | |
| result = json.loads(response.choices[0].message.content) | |
| decisions = result.get("decisions", []) | |
| if not isinstance(decisions, list): | |
| decisions = [] | |
| if len(decisions) < batch_size: | |
| decisions += [random.choice([0, 1]) for _ in range(batch_size - len(decisions))] | |
| return decisions[:batch_size] | |
| except Exception as e: | |
| print(f"[LLM ROUTER ERROR] API execution failed: {e}") | |
| return [random.choice([0, 1]) for _ in range(batch_size)] | |
| # ============================================================================== | |
| # PHASE 3: API ENDPOINTS (The Data Pipeline) | |
| # ============================================================================== | |
| async def get_state(): | |
| # FIX: Check the dynamically tracked instance instead of the old static one | |
| if not active_env_instance: | |
| return {"status": "FALLBACK_MOCK_MODE", "health": system_health, "accuracy": 0.0, "latency_us": 0.0, "throughput_m": 0.0, "buffer_saturation": 0.0, "active_count": 0, "total_ingested": 0, "step_count": 0, "difficulty": "EASY", "metrics": {"tp": 0, "tn": 0, "fp": 0, "fn": 0}} | |
| tp = getattr(active_env_instance.state, 'last_tp', 0) | |
| fp = getattr(active_env_instance.state, 'last_fp', 0) | |
| tn = getattr(active_env_instance.state, 'last_tn', 0) | |
| fn = getattr(active_env_instance.state, 'last_fn', 0) | |
| accuracy = tp / (tp + fp) if (tp + fp) > 0 else 0.0 | |
| latency_us = app_metrics["last_step_latency_us"] | |
| return { | |
| "status": "NATIVE_RECON_ONLINE", | |
| "health": system_health, | |
| "accuracy": round(accuracy, 4), | |
| "latency_us": round(latency_us, 3), | |
| "latency_source": "grounded_app_middleware", | |
| "throughput_m": round((40 * 1e6) / (latency_us * 1000), 2) if latency_us > 0 else 0.0, | |
| "active_count": active_env_instance.engine.last_expired_count, | |
| "total_ingested": active_env_instance.engine.total_ingested, | |
| "ring_buffer_size": active_env_instance.engine.ring_buffer_size, | |
| "buffer_saturation": min(100.0, (active_env_instance.engine.last_expired_count / 100.0) * 100), | |
| "step_count": active_env_instance.state.step_count, | |
| "metrics": {"tp": tp, "tn": tn, "fp": fp, "fn": fn}, | |
| "difficulty": getattr(active_env_instance, 'difficulty', "EASY") | |
| } | |
| async def get_dashboard_action(req: ActionRequest): | |
| batch_size = 40 | |
| if req.action_type == "perfect": | |
| decisions = [1] * batch_size | |
| elif req.action_type == "llm": | |
| api_key = llm_session.get("api_key") | |
| base_url = llm_session.get("base_url") | |
| model_name = llm_session.get("model_name") | |
| if not model_name and llm_session.get("available_models"): | |
| model_name = llm_session["available_models"][0] | |
| if not api_key or not model_name: | |
| raise HTTPException(status_code=400, detail="LLM Provider not authenticated. Please inject an API key.") | |
| decisions = await execute_llm_step(api_key, base_url, model_name, batch_size) | |
| else: | |
| decisions = [random.choice([0, 1]) for _ in range(batch_size)] | |
| return {"decisions": decisions} | |
| class DashboardStepRequest(BaseModel): | |
| """Action payload for the dashboard-native step endpoint.""" | |
| decisions: List[int] | |
| async def dashboard_step(req: DashboardStepRequest): | |
| """ | |
| Dashboard-native step: runs on the SINGLETON engine (warm, with real trade data), | |
| not on the OpenEnv /step route which creates a cold engine per request. | |
| This is what the three dashboard buttons (OPTIMAL / STRESS / LLM) call so that | |
| rewards reflect actual confusion-matrix scoring rather than the 0.01 floor. | |
| """ | |
| if not active_env_instance or not NATIVE_VERIFIED: | |
| raise HTTPException(status_code=503, detail="Native engine not available") | |
| from models import AuditorAction | |
| action = AuditorAction(decisions=req.decisions) | |
| obs = active_env_instance.step(action) | |
| return { | |
| "reward": obs.reward, | |
| "done": obs.done, | |
| "step_count": active_env_instance.state.step_count, | |
| "features_shape": [len(obs.features), len(obs.features[0]) if obs.features else 0], | |
| } | |
| async def dashboard_reset(): | |
| """ | |
| Reset the dashboard singleton: re-seeds the ring buffer with fresh trade data. | |
| Called by the [FLUSH_SPSC_BUFFER] button in the dashboard JS. | |
| """ | |
| if not active_env_instance or not NATIVE_VERIFIED: | |
| raise HTTPException(status_code=503, detail="Native engine not available") | |
| active_env_instance.reset() | |
| return {"status": "ok", "step_count": active_env_instance.state.step_count} | |
| async def config_llm(cfg: LLMConfig): | |
| api_key = cfg.api_key | |
| if not api_key: | |
| api_key = llm_session.get("api_key") | |
| if not api_key: | |
| raise HTTPException(status_code=400, detail="API Key cannot be blank") | |
| base_url = "https://router.huggingface.co/v1" | |
| if api_key.startswith("AIza"): | |
| base_url = "https://generativelanguage.googleapis.com/v1beta/openai/" | |
| elif api_key.startswith("sk-ant"): | |
| base_url = "https://api.anthropic.com/v1" | |
| try: | |
| client = AsyncOpenAI(base_url=base_url, api_key=api_key, max_retries=2) | |
| try: | |
| response = await client.models.list() | |
| model_list = [m.id for m in response.data] | |
| except Exception as e: | |
| if "googleapis" in base_url: | |
| model_list = ["gemini-1.5-flash", "gemini-1.5-pro", "gemini-2.0-flash"] | |
| else: | |
| raise e | |
| llm_session["api_key"] = api_key | |
| llm_session["base_url"] = base_url | |
| llm_session["available_models"] = model_list | |
| system_health["key_validated"] = True | |
| system_health["model_detected"] = len(model_list) > 0 | |
| if cfg.model_name and cfg.model_name in model_list: | |
| llm_session["model_name"] = cfg.model_name | |
| os.environ["MODEL_NAME"] = cfg.model_name | |
| os.environ["LLM_API_KEY"] = api_key | |
| os.environ["API_BASE_URL"] = base_url | |
| system_health["connected"] = True | |
| msg = f"Injected {cfg.model_name} credentials." | |
| else: | |
| msg = "Key validated. Models discovered via OpenAI spec." | |
| return {"status": "success", "models": model_list, "message": msg} | |
| except Exception as e: | |
| system_health["key_validated"] = False | |
| system_health["model_detected"] = False | |
| system_health["connected"] = False | |
| return {"status": "error", "message": f"Validation failed: {str(e)}"} | |
| async def config_default(): | |
| token = os.getenv("HF_TOKEN") | |
| if not token: | |
| return {"status": "error", "message": "No HF_TOKEN found in system environment"} | |
| return await config_llm(LLMConfig(api_key=token, base_url="https://router.huggingface.co/v1")) | |
| async def set_difficulty(cfg: DifficultyConfig): | |
| if active_env_instance and NATIVE_VERIFIED: | |
| os.environ["TASK_ID"] = cfg.level.lower() | |
| if "easy" in cfg.level.lower(): | |
| active_env_instance.difficulty = hft_mod.Difficulty.EASY | |
| elif "medium" in cfg.level.lower(): | |
| active_env_instance.difficulty = hft_mod.Difficulty.MEDIUM | |
| else: | |
| active_env_instance.difficulty = hft_mod.Difficulty.HARD | |
| return {"status": "success", "difficulty": cfg.level} | |
| return {"status": "error", "message": "Engine not loaded"} | |
| # ============================================================================== | |
| # GRADER ENDPOINT — Required by the competition evaluator | |
| # The evaluator calls GET /grader to discover tasks and retrieve their scores. | |
| # Each score MUST be strictly between 0.0 and 1.0 (exclusive). | |
| # ============================================================================== | |
| _GRADER_SCORE_FLOOR = 0.01 # returned when no episode data is available | |
| _GRADER_SCORE_CAP = 0.99 | |
| def _safe_grade(grader, state) -> float: | |
| """Run grader.grade() and enforce the (0, 1) exclusive boundary contract.""" | |
| try: | |
| raw = grader.grade(state=state) | |
| except Exception: | |
| raw = _GRADER_SCORE_FLOOR | |
| return float(max(_GRADER_SCORE_FLOOR, min(_GRADER_SCORE_CAP, raw))) | |
| async def get_grader(): | |
| """ | |
| Competition evaluation endpoint. | |
| Returns the per-task grader names and their current scores so the | |
| hackathon evaluator can verify: | |
| 1. At least 3 tasks have custom graders (not 'reward'). | |
| 2. Every score is strictly in the open interval (0, 1). | |
| """ | |
| # Use the live singleton state when available; fall back to floor score. | |
| state = active_env_instance.state if active_env_instance else None | |
| easy_score = _safe_grade(_grader_easy, state) if _grader_easy else _GRADER_SCORE_FLOOR | |
| medium_score = _safe_grade(_grader_medium, state) if _grader_medium else _GRADER_SCORE_FLOOR | |
| hard_score = _safe_grade(_grader_hard, state) if _grader_hard else _GRADER_SCORE_FLOOR | |
| return { | |
| "tasks": [ | |
| { | |
| "task_id": "anomaly_detection_easy", | |
| "grader": "graders.grader_detection:EasyDetectionGrader", | |
| "score": round(easy_score, 4), | |
| "max_steps": 5, | |
| "difficulty": "easy", | |
| }, | |
| { | |
| "task_id": "anomaly_detection_medium", | |
| "grader": "graders.grader_classification:MediumClassificationGrader", | |
| "score": round(medium_score, 4), | |
| "max_steps": 10, | |
| "difficulty": "medium", | |
| }, | |
| { | |
| "task_id": "anomaly_detection_hard", | |
| "grader": "graders.grader_fix:HardFixGrader", | |
| "score": round(hard_score, 4), | |
| "max_steps": 20, | |
| "difficulty": "hard", | |
| }, | |
| ], | |
| "grader_count": 3, | |
| "all_scores_valid": all( | |
| 0.0 < s < 1.0 | |
| for s in [easy_score, medium_score, hard_score] | |
| ), | |
| } | |
| async def websocket_telemetry(websocket: WebSocket): | |
| await websocket.accept() | |
| try: | |
| while True: | |
| if active_env_instance and NATIVE_VERIFIED: | |
| data = { | |
| "active_count": active_env_instance.engine.active_count, | |
| "total_ingested": active_env_instance.engine.total_ingested, | |
| "ring_buffer_size": active_env_instance.engine.ring_buffer_size, | |
| "pool_capacity": active_env_instance.engine.pool_capacity, | |
| "latency_us": round(app_metrics["last_step_latency_us"], 3), | |
| "status": "NATIVE_ACTIVE" | |
| } | |
| else: | |
| data = {"status": "MOCK_FALLBACK", "latency_us": 0.0} | |
| await websocket.send_json(data) | |
| await asyncio.sleep(0.5) | |
| except WebSocketDisconnect: | |
| pass | |
| # ============================================================================== | |
| # PHASE 4: THE JUDGE-AUTHORITY DASHBOARD | |
| # ============================================================================== | |
| async def root_dashboard(): | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PayGorn | HFT Command Center</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg: #0B0E14; --sidebar: #0D1117; --surface: #151921; | |
| --border: #30363d; --text-main: #E6EDF3; --text-dim: #8B949E; | |
| --accent-blue: #2F81F7; --accent-emerald: #00FF9D; --accent-red: #F85149; --accent-amber: #FFB86C; | |
| --font-ui: 'Inter', sans-serif; --font-data: 'JetBrains Mono', monospace; | |
| } | |
| * { box-sizing: border-box; } | |
| body { background: var(--bg); color: var(--text-main); font-family: var(--font-ui); margin: 0; display: flex; height: 100vh; overflow: hidden; } | |
| ::-webkit-scrollbar { width: 8px; } | |
| ::-webkit-scrollbar-track { background: var(--bg); } | |
| ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } | |
| h1, h2, h3, h4 { margin-top: 0; font-weight: 600; letter-spacing: -0.5px; } | |
| .data-text { font-family: var(--font-data); } | |
| .sidebar { width: 280px; background: var(--sidebar); border-right: 1px solid var(--border); display: flex; flex-direction: column; padding: 24px; flex-shrink: 0; z-index: 10;} | |
| .main { flex: 1; overflow-y: auto; padding: 40px; scroll-behavior: smooth; position: relative;} | |
| .container { max-width: 1000px; margin: 0 auto; } | |
| .logo { font-size: 18px; font-weight: 800; color: var(--text-main); letter-spacing: 1px; } | |
| .logo span { color: var(--accent-emerald); } | |
| .version { font-size: 10px; color: var(--text-dim); margin-top: 4px; font-family: var(--font-data); } | |
| .auth-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 30px; margin-bottom: 20px;} | |
| .auth-item { font-size: 10px; font-family: var(--font-data); padding: 6px; border: 1px solid var(--border); border-radius: 4px; display: flex; align-items: center; transition: 0.3s;} | |
| .status-icon { margin-right: 6px; font-weight: bold; } | |
| .verified { color: var(--accent-emerald); border-color: var(--accent-emerald); background: rgba(0,255,157,0.05);} | |
| .failed { color: var(--text-dim); border-color: var(--border); } | |
| .config-panel { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 16px; margin-bottom: 20px; } | |
| .config-panel-title { font-size: 10px; color: var(--text-dim); text-transform: uppercase; margin-bottom: 12px; font-family: var(--font-data); } | |
| .config-input, .config-select { width: 100%; background: var(--sidebar); border: 1px solid var(--border); color: var(--text-main); font-family: var(--font-data); font-size: 11px; padding: 8px; margin-bottom: 8px; border-radius: 4px; outline: none;} | |
| .config-input:focus, .config-select:focus { border-color: var(--accent-blue); } | |
| .header-top { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); padding-bottom: 20px; margin-bottom: 32px; } | |
| .status-indicator { display: flex; align-items: center; font-size: 11px; font-family: var(--font-data); color: var(--text-dim); } | |
| .pulse-dot { width: 8px; height: 8px; background: var(--accent-emerald); border-radius: 50%; margin-right: 8px; box-shadow: 0 0 10px var(--accent-emerald); animation: pulse 2s infinite; } | |
| @keyframes pulse { 0% { opacity: 0.3; } 50% { opacity: 1; } 100% { opacity: 0.3; } } | |
| .panel { background: var(--surface); border: 1px solid var(--border); border-radius: 4px; padding: 24px; margin-bottom: 24px; } | |
| .panel-title { font-size: 11px; color: var(--text-dim); text-transform: uppercase; margin-bottom: 16px; font-family: var(--font-data); letter-spacing: 1px; border-bottom: 1px solid var(--border); padding-bottom: 8px;} | |
| .hero h1 { font-size: 32px; letter-spacing: -1px; } | |
| .hero strong { color: var(--text-main); font-weight: 600; } | |
| .telemetry-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; } | |
| .metric-card { background: var(--bg); border: 1px solid var(--border); padding: 16px; border-radius: 4px; } | |
| .metric-label { font-size: 11px; color: var(--text-dim); font-family: var(--font-data); margin-bottom: 8px; } | |
| .metric-val { font-size: 26px; font-weight: 700; color: var(--text-main); font-family: var(--font-data); display: flex; align-items: baseline; transition: all 0.3s ease; } | |
| .metric-unit { font-size: 12px; color: var(--text-dim); margin-left: 6px; font-weight: 400; } | |
| .halo-glow { color: var(--accent-emerald) !important; text-shadow: 0 0 15px rgba(0, 255, 157, 0.5); } | |
| .threat-glow { color: var(--accent-red) !important; } | |
| .amber-glow { color: var(--accent-amber) !important; } | |
| .btn-group { display: flex; gap: 12px; flex-wrap: wrap; } | |
| .btn { background: var(--surface); border: 1px solid var(--border); color: var(--text-main); padding: 10px 16px; border-radius: 4px; font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.1s ease; font-family: var(--font-ui); } | |
| .btn:hover { background: #1e242d; border-color: #555; } | |
| .btn:active { transform: translateY(2px); border-color: var(--text-main); } | |
| .btn-action { color: var(--text-main); border-color: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.05);} | |
| .btn-action:hover { border-color: var(--text-main); background: rgba(255, 255, 255, 0.1); } | |
| .btn-threat { color: var(--accent-red); border-color: rgba(248, 81, 73, 0.3); } | |
| .btn-threat:hover { border-color: var(--accent-red); background: rgba(248, 81, 73, 0.05); } | |
| .btn-easy { color: var(--accent-emerald) !important; border-color: var(--accent-emerald) !important; background: rgba(0, 255, 157, 0.05); } | |
| .btn-medium { color: var(--accent-amber) !important; border-color: var(--accent-amber) !important; background: rgba(255, 184, 108, 0.05); } | |
| .btn-hard { color: var(--accent-red) !important; border-color: var(--accent-red) !important; } | |
| .pulse-container { display: flex; align-items: flex-end; gap: 6px; height: 50px; margin-top: 10px; } | |
| .pulse-bar { flex: 1; background: var(--border); transition: height 0.2s ease, background 0.3s; min-width: 12px; border-radius: 2px 2px 0 0;} | |
| .bar-tp { background: var(--accent-emerald); } | |
| .bar-fp { background: var(--accent-red); } | |
| .data-table { width: 100%; border-collapse: collapse; font-family: var(--font-data); font-size: 12px; } | |
| .data-table th, .data-table td { text-align: left; padding: 10px; border-bottom: 1px solid var(--border); } | |
| .data-table th { color: var(--text-dim); font-weight: 400; } | |
| .console { background: #050608; border: 1px solid #111; padding: 16px; font-family: var(--font-data); font-size: 11px; color: var(--text-dim); height: 200px; overflow-y: auto; border-radius: 4px; position: relative;} | |
| .log-info { color: #888; margin-bottom: 4px; } | |
| .log-success { color: var(--accent-emerald); margin-bottom: 4px; } | |
| .log-warn { color: var(--accent-amber); margin-bottom: 4px; } | |
| .log-err { color: var(--accent-red); margin-bottom: 4px; } | |
| .crt-active::after { | |
| content: " "; position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 5; | |
| background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.1) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.03), rgba(0, 255, 0, 0.01), rgba(0, 0, 255, 0.03)); | |
| background-size: 100% 3px, 3px 100%; pointer-events: none; | |
| } | |
| body.hard-mode .main::before { | |
| content: ""; position: fixed; top: 0; left: 0; width: 100%; height: 100%; | |
| box-shadow: inset 0 0 100px rgba(248, 81, 73, 0.05); pointer-events: none; z-index: 100; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <aside class="sidebar"> | |
| <div class="logo">PAYGORN<span>.AI</span></div> | |
| <div class="version">BUILD: C++20_NANO_BIND | O-ENV 1.0</div> | |
| <div style="margin-top: 8px; font-size: 9px; color: var(--accent-emerald); border: 1px solid var(--accent-emerald); padding: 4px; border-radius: 4px; display: inline-block; font-family: var(--font-data);">NATIVE ENGINE ACTIVE</div> | |
| <div class="auth-grid"> | |
| <div id="auth-so" class="auth-item"><span class="status-icon">❌</span> BINARY</div> | |
| <div id="auth-key" class="auth-item"><span class="status-icon">❌</span> KEY</div> | |
| <div id="auth-model" class="auth-item"><span class="status-icon">❌</span> MODEL</div> | |
| <div id="auth-conn" class="auth-item"><span class="status-icon">❌</span> CONN</div> | |
| </div> | |
| <div class="config-panel"> | |
| <div class="config-panel-title">LLM_OVERRIDE_ROUTER</div> | |
| <button class="btn" style="width:100%; margin-bottom:12px; font-size:10px; color: var(--accent-emerald); border-color: rgba(0, 255, 157, 0.3); background: rgba(0, 255, 157, 0.05);" onclick="useDefault()">[USE DEFAULT HF_TOKEN]</button> | |
| <input id="api-key" class="config-input" type="password" placeholder="Provider API Key" onchange="discoverModels()"> | |
| <select id="model-select" class="config-select"> | |
| <option value="">-- Discovering Models --</option> | |
| </select> | |
| <button class="btn btn-action" style="width:100%; font-size:11px; padding: 8px;" onclick="saveConfig()">INJECT CONTEXT</button> | |
| </div> | |
| <nav style="margin-top: 10px; display: flex; flex-direction: column; gap: 8px;"> | |
| <div class="config-panel-title" style="margin-bottom: 4px;">CONTROL_PLANE</div> | |
| <button class="btn" onclick="executeReset()">[0] FLUSH_SPSC_BUFFER</button> | |
| <button class="btn" style="color: var(--accent-emerald); border-color: rgba(0, 255, 157, 0.3); background: rgba(0, 255, 157, 0.05);" onclick="executeStep('perfect')">[1] STEP_AGENT_OPTIMAL</button> | |
| <button class="btn btn-threat" onclick="executeStep('random')">[2] STEP_AGENT_STRESS</button> | |
| <button class="btn btn-action" style="color: var(--accent-blue); border-color: rgba(47, 129, 247, 0.3); background: rgba(47, 129, 247, 0.05);" onclick="executeStep('llm')">[3] STEP_AGENT_LLM</button> | |
| </nav> | |
| </aside> | |
| <main class="main" id="main-area"> | |
| <div class="container"> | |
| <div class="header-top"> | |
| <div style="font-family: var(--font-data); font-size: 12px; color: var(--text-dim);"> | |
| > SPSC_RING_BUFFER: <span style="color:var(--accent-emerald)">ACTIVE</span> | |
| </div> | |
| <div class="status-indicator"> | |
| <div class="pulse-dot"></div> | |
| <span id="status-text">SYSTEM: INITIALIZING...</span> | |
| </div> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 32px; gap: 24px;"> | |
| <div class="hero" style="flex: 1;"> | |
| <h1 style="margin:0 0 8px 0;">HFT Audit Command Center</h1> | |
| <p style="color: var(--text-dim); font-size: 14px; max-width: 650px; line-height: 1.6; margin-bottom: 16px;"> | |
| An OpenEnv-compliant RL environment simulating high-frequency adversarial trading. Built on a native <strong>C++20 lock-free ring buffer</strong>, it maps microsecond telemetry directly to the agent's matrix for deterministic, zero-copy evaluation. | |
| </p> | |
| </div> | |
| <div class="btn-group" style="align-self: flex-start; margin-top: 6px;"> | |
| <button class="btn btn-easy" onclick="setDifficulty('EASY')">EASY</button> | |
| <button class="btn btn-medium" onclick="setDifficulty('MEDIUM')">MEDIUM</button> | |
| <button class="btn btn-threat" onclick="setDifficulty('HARD_ADVERSARIAL')">HARD_ADVERSARIAL</button> | |
| </div> | |
| </div> | |
| <div class="telemetry-grid"> | |
| <div class="metric-card"> | |
| <div class="metric-label">ENGINE_LATENCY</div> | |
| <div class="metric-val" id="val-latency">0.000<span class="metric-unit">μs</span></div> | |
| </div> | |
| <div class="metric-card"> | |
| <div class="metric-label">RECON_INTEGRITY (ACC)</div> | |
| <div class="metric-val" id="val-accuracy">0.00<span class="metric-unit">ratio</span></div> | |
| </div> | |
| <div class="metric-card"> | |
| <div class="metric-label">THROUGHPUT</div> | |
| <div class="metric-val" id="val-throughput">0.00<span class="metric-unit">M/s</span></div> | |
| </div> | |
| <div class="metric-card"> | |
| <div class="metric-label">BUFFER_SAT</div> | |
| <div class="metric-val" id="val-saturation">0.0<span class="metric-unit">%</span></div> | |
| </div> | |
| <div class="metric-card"> | |
| <div class="metric-label">ACTIVE_TRADES</div> | |
| <div class="metric-val" id="val-active-count">0</div> | |
| </div> | |
| <div class="metric-card"> | |
| <div class="metric-label">TOTAL_LOGS</div> | |
| <div class="metric-val" id="val-total-ingested">0</div> | |
| </div> | |
| </div> | |
| <div style="display: grid; grid-template-columns: 2fr 1fr; gap: 24px;"> | |
| <section class="panel" style="margin-bottom:0;"> | |
| <div class="panel-title">Execution Ledger</div> | |
| <table class="data-table" id="ledger-table"> | |
| <thead> | |
| <tr> | |
| <th>STEP</th> | |
| <th>OBS_STATE</th> | |
| <th>ACTION</th> | |
| <th>REWARD</th> | |
| <th>DONE</th> | |
| </tr> | |
| </thead> | |
| <tbody id="ledger-body"></tbody> | |
| </table> | |
| </section> | |
| <section class="panel" style="margin-bottom:0;"> | |
| <div class="panel-title">CLASSIFICATION_PULSE</div> | |
| <div class="pulse-container" id="pulse-bars"> | |
| <div class="pulse-bar bar-tp" style="height: 0%"></div> | |
| <div class="pulse-bar" style="height: 0%"></div> | |
| <div class="pulse-bar bar-fp" style="height: 0%"></div> | |
| <div class="pulse-bar" style="height: 0%"></div> | |
| </div> | |
| <div style="display:flex; justify-content:space-between; font-family:var(--font-data); font-size:10px; color:var(--text-dim); margin-top:8px;"> | |
| <span>TP</span><span>TN</span><span>FP</span><span>FN</span> | |
| </div> | |
| </section> | |
| </div> | |
| <section class="panel" style="padding: 0; overflow: hidden; margin-top:24px;"> | |
| <div class="panel-title" style="padding: 24px 24px 0;">Raw Telemetry Stream</div> | |
| <div class="console" id="console-out"> | |
| <div class="log-info">[SYS] Boot sequence initialized...</div> | |
| <div class="log-info">[SYS] Bridging C++ Memory Pool...</div> | |
| </div> | |
| </section> | |
| </div> | |
| </main> | |
| <script> | |
| const consoleOut = document.getElementById('console-out'); | |
| const ledgerBody = document.getElementById('ledger-body'); | |
| let usingDefaultToken = false; | |
| function logMsg(msg, type='info') { | |
| const div = document.createElement('div'); | |
| div.className = 'log-' + type; | |
| const time = new Date().toISOString().split('T')[1].slice(0,12); | |
| div.innerText = `[${time}] ${msg}`; | |
| consoleOut.appendChild(div); | |
| consoleOut.scrollTop = consoleOut.scrollHeight; | |
| } | |
| async function updateState() { | |
| try { | |
| const res = await fetch('/dashboard/state'); | |
| const data = await res.json(); | |
| const h = data.health; | |
| const updateBadge = (id, val) => { | |
| const el = document.getElementById(id); | |
| el.className = val ? 'auth-item verified' : 'auth-item failed'; | |
| el.querySelector('.status-icon').innerText = val ? '✅' : '❌'; | |
| }; | |
| updateBadge('auth-so', h.so_found); | |
| updateBadge('auth-key', h.key_validated); | |
| updateBadge('auth-model', h.model_detected); | |
| updateBadge('auth-conn', h.connected); | |
| document.getElementById('status-text').innerText = `SYSTEM: ${data.status} | [${data.difficulty || 'EASY'}]`; | |
| document.getElementById('val-latency').innerHTML = `${data.latency_us.toFixed(3)}<span class="metric-unit">μs</span>`; | |
| document.getElementById('val-throughput').innerHTML = `${data.throughput_m.toFixed(2)}<span class="metric-unit">M/s</span>`; | |
| const accEl = document.getElementById('val-accuracy'); | |
| accEl.innerHTML = `${data.accuracy.toFixed(2)}<span class="metric-unit">ratio</span>`; | |
| if (data.accuracy >= 0.90) { | |
| accEl.classList.add('halo-glow'); accEl.classList.remove('threat-glow'); | |
| } else if (data.accuracy < 0.40) { | |
| accEl.classList.add('threat-glow'); accEl.classList.remove('halo-glow'); | |
| } else { | |
| accEl.classList.remove('halo-glow', 'threat-glow'); | |
| } | |
| const satEl = document.getElementById('val-saturation'); | |
| satEl.innerHTML = `${data.buffer_saturation.toFixed(1)}<span class="metric-unit">%</span>`; | |
| if(data.buffer_saturation > 70) satEl.classList.add('amber-glow'); else satEl.classList.remove('amber-glow'); | |
| document.getElementById('val-active-count').innerText = data.active_count; | |
| document.getElementById('val-total-ingested').innerText = data.total_ingested; | |
| const m = data.metrics; | |
| const bars = document.getElementById('pulse-bars').children; | |
| const max = Math.max(m.tp, m.tn, m.fp, m.fn, 1); | |
| bars[0].style.height = `${(m.tp / max) * 100}%`; | |
| bars[1].style.height = `${(m.tn / max) * 100}%`; | |
| bars[2].style.height = `${(m.fp / max) * 100}%`; | |
| bars[3].style.height = `${(m.fn / max) * 100}%`; | |
| const body = document.body; | |
| if(data.difficulty && data.difficulty.includes('HARD')) { | |
| consoleOut.classList.add('crt-active'); | |
| body.classList.add('hard-mode'); | |
| } else { | |
| consoleOut.classList.remove('crt-active'); | |
| body.classList.remove('hard-mode'); | |
| } | |
| } catch(e) {} | |
| } | |
| async function discoverModels() { | |
| const key = document.getElementById('api-key').value; | |
| if(!key) return; | |
| usingDefaultToken = false; | |
| logMsg("Validating key and mapping models...", "info"); | |
| const res = await fetch('/config/llm', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({api_key: key}) | |
| }); | |
| const data = await res.json(); | |
| if(data.status === 'success') { | |
| const select = document.getElementById('model-select'); | |
| select.innerHTML = ''; | |
| data.models.forEach(m => { | |
| const opt = document.createElement('option'); | |
| opt.value = m; opt.innerText = m; | |
| select.appendChild(opt); | |
| }); | |
| logMsg("Provider models mapped.", "success"); | |
| updateState(); | |
| } else { | |
| logMsg("Validation failed: " + data.message, "err"); | |
| } | |
| } | |
| async function saveConfig() { | |
| const key = document.getElementById('api-key').value; | |
| const model = document.getElementById('model-select').value; | |
| if (!model || (!key && !usingDefaultToken)) { | |
| logMsg("Key/Model missing.", "err"); return; | |
| } | |
| const res = await fetch('/config/llm', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({api_key: key || "", model_name: model}) | |
| }); | |
| const data = await res.json(); | |
| if(data.status === 'success') { | |
| logMsg(data.message, "success"); updateState(); | |
| } else { | |
| logMsg(data.message, "err"); | |
| } | |
| } | |
| async function useDefault() { | |
| logMsg("Pulling root token...", "info"); | |
| const res = await fetch('/config/default', {method: 'POST'}); | |
| const data = await res.json(); | |
| if(data.status === 'success') { | |
| usingDefaultToken = true; | |
| const select = document.getElementById('model-select'); | |
| select.innerHTML = ''; | |
| data.models.forEach(m => { | |
| const opt = document.createElement('option'); | |
| opt.value = m; opt.innerText = m; | |
| select.appendChild(opt); | |
| }); | |
| logMsg("Zero-config active.", "success"); updateState(); | |
| } else { | |
| logMsg(data.message, "err"); | |
| } | |
| } | |
| async function setDifficulty(lvl) { | |
| logMsg(`Mutating task ID to ${lvl}...`, "warn"); | |
| await fetch('/config/difficulty', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({level: lvl}) | |
| }); | |
| updateState(); | |
| } | |
| async function executeReset() { | |
| logMsg("SPSC_BUFFER_FLUSHING...", "warn"); | |
| try { | |
| await fetch('/dashboard/reset', {method: 'POST'}); | |
| ledgerBody.innerHTML = ''; | |
| updateState(); | |
| logMsg("Memory pool purged and re-seeded.", "success"); | |
| } catch(e) { | |
| logMsg("Reset failed: " + e.message, "err"); | |
| } | |
| } | |
| async function executeStep(actionType) { | |
| logMsg(`Generating decisions via Agent: ${actionType.toUpperCase()}`, "info"); | |
| try { | |
| const actionRes = await fetch('/dashboard/get_action', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({action_type: actionType}) | |
| }); | |
| if (!actionRes.ok) { | |
| const errData = await actionRes.json(); | |
| logMsg("LLM Error: " + (errData.detail || "Failed to generate decisions"), "err"); | |
| return; | |
| } | |
| const actionData = await actionRes.json(); | |
| if(!actionData.decisions) { logMsg("Decision matrix generation failed.", "err"); return; } | |
| logMsg(`Executing Step with ${actionData.decisions.length} decisions...`, "info"); | |
| // POST to /dashboard/step (warm singleton) NOT /step (cold factory engine) | |
| const res = await fetch('/dashboard/step', { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({ decisions: actionData.decisions }) | |
| }); | |
| if (!res.ok) { | |
| const errorData = await res.json(); | |
| logMsg(`Server Error: ${res.status} — ${errorData.detail || 'check logs'}`, "err"); | |
| return; | |
| } | |
| const data = await res.json(); | |
| const reward = data.reward ?? 0.0; | |
| const done = data.done ?? false; | |
| const step = data.step_count ?? 'N/A'; | |
| logMsg(`[RECON] Reward: ${reward.toFixed(4)} | Step: ${step}`, reward >= 0.8 ? 'success' : 'warn'); | |
| const row = document.createElement('tr'); | |
| row.innerHTML = ` | |
| <td>${step}</td> | |
| <td class="data-text">Vector[40]</td> | |
| <td class="data-text">${actionType.toUpperCase()}</td> | |
| <td class="${reward >= 0.8 ? 'halo-glow' : ''}">${reward.toFixed(4)}</td> | |
| <td class="${done ? 'threat-glow' : ''}">${done}</td> | |
| `; | |
| if(ledgerBody.children.length >= 5) { ledgerBody.removeChild(ledgerBody.firstChild); } | |
| ledgerBody.appendChild(row); | |
| updateState(); | |
| } catch(e) { | |
| logMsg("Step Execution Error: " + e.message, "err"); | |
| } | |
| } | |
| // Auto-Reset the environment on boot so it actually has data to process, | |
| // then try to authenticate with the default HF_TOKEN | |
| window.addEventListener('DOMContentLoaded', async () => { | |
| logMsg("Auto-initializing environment engine...", "info"); | |
| await executeReset(); | |
| logMsg("Attempting default token auth...", "info"); | |
| await useDefault(); | |
| setInterval(updateState, 1000); | |
| updateState(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| return HTMLResponse(content=html_content) | |
| def main(): | |
| port = int(os.getenv("PORT", 7860)) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |
| if __name__ == "__main__": | |
| main() |