"""HF Spaces server: ClaimSense adjudication gym + lightweight dashboard.
Run with::
uvicorn space_app:app --host 0.0.0.0 --port 7860
Adds three things on top of the OpenEnv FastAPI scaffolding:
1. ``GET /`` — an HTML dashboard so the Space's landing page looks
like a product, not raw JSON.
2. ``GET /api`` — the JSON metadata block that used to live at ``/``.
3. ``GET /info`` — verbose env description used by notebooks/training.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
# Make local sibling modules importable when running inside the Space's
# Docker image (where the working directory is ``/app``).
sys.path.insert(0, str(Path(__file__).resolve().parent))
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from openenv.core.env_server import create_fastapi_app
from models import AdjudicatorAction, AdjudicatorObservation
from server.claims_environment import AdjudicationGym
# ---------------------------------------------------------------------------
# Compose the FastAPI app
# ---------------------------------------------------------------------------
app: FastAPI = create_fastapi_app(
AdjudicationGym, AdjudicatorAction, AdjudicatorObservation
)
# Allow notebooks/clients on any origin to call us during demos.
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ---------------------------------------------------------------------------
# Dashboard HTML
# ---------------------------------------------------------------------------
DASHBOARD_HTML = """
ClaimSense AI · Adjudication Gym
🛡️ ClaimSense AI
checking…
Space akhiilll/claims-env
OpenEnv Hackathon · Statement 3.1 · Scaler AI Labs
An adjudication gym for training LLM agents to triage insurance
claims — partial observability, eight curated cases, fraud signals,
and bank-transaction verification.
Endpoints
HTTP base
WebSocket
ResetPOST /reset
StepPOST /step
StateGET /state
HealthGET /health
Reward shaping
Correct decision+10
Fraud caught (deny)+5
Plaid discrepancy surfaced+2
Fast resolution (≤4 steps)+1
Wrong decision-5
Fraud missed (approve)-10
Query cost-0.1 … -0.5
Curated case set (8)
Routine fender-bender
Burst pipe (capped)
Staged accident
External flood (excluded)
Six-figure house fire
Inflated stolen vehicle
Slip-and-fall liability
Lapsed policy
Action vocabulary (10)
loading…
Live API probe
click a button above to call the API
"""
# ---------------------------------------------------------------------------
# Routes
# ---------------------------------------------------------------------------
@app.get("/", response_class=HTMLResponse)
async def root_dashboard() -> HTMLResponse:
"""Single-page dashboard served at the Space root."""
return HTMLResponse(content=DASHBOARD_HTML)
@app.get("/api")
async def api_metadata() -> dict[str, object]:
"""Machine-readable metadata (was at ``/`` historically)."""
return {
"name": "ClaimSense Adjudication Gym",
"version": "1.1.0",
"hackathon": "OpenEnv Hackathon - Cerebral Valley",
"problem_statement": "3.1 - Professional Tasks (World Modeling)",
"partner_theme": "Scaler AI Labs - Enterprise Workflows",
"status": "running",
"valid_actions": list(AdjudicationGym.VALID_ACTIONS),
"endpoints": {
"health": "/health",
"info": "/info",
"reset": "POST /reset",
"step": "POST /step",
"state": "GET /state",
"websocket": "/ws",
},
}
@app.get("/info")
async def long_info() -> dict[str, object]:
"""Verbose description used by notebooks for documentation."""
return {
"name": "ClaimSense Adjudication Gym",
"version": "1.1.0",
"description": (
"RL environment for training LLM agents to triage insurance "
"claims through a sequence of evidence-gathering steps and a "
"final verdict."
),
"problem_statement": "3.1 - Professional Tasks (World Modeling)",
"partner_theme": "Scaler AI Labs - Enterprise Workflows",
"features": [
"Partial observability — agent must query for facts",
"Multi-step decision making with terminal verdicts",
"Fraud detection signals and Plaid-style transaction audit",
"Business rule enforcement (deductibles, exclusions, lapsed)",
"Enterprise-flavoured workflow with escalation paths",
],
"valid_actions": list(AdjudicationGym.VALID_ACTIONS),
"action_costs": AdjudicationGym.ACTION_TIME_COSTS,
"reward_structure": {
"correct_decision": "+10",
"wrong_decision": "-5",
"fraud_caught": "+5",
"fraud_missed": "-10",
"plaid_discrepancy_found": "+2",
"query_cost": "-0.1 to -0.5",
"fast_resolution_bonus": "+1 (≤ 4 steps)",
"slow_resolution_penalty": "-0.2 per step beyond 8",
},
"scenarios": 8,
"scenario_types": [
"Routine approval",
"Partial settlement (capped)",
"Staged accident fraud",
"Excluded coverage denial",
"Six-figure escalation",
"Inflated theft fraud",
"Liability slip-and-fall",
"Lapsed-policy denial",
],
}
@app.get("/scenarios")
async def list_scenarios() -> dict[str, object]:
"""Enumerate the curated case library."""
from server.mock_systems import CASE_LIBRARY # local to avoid import cycle
return {
"total_scenarios": len(CASE_LIBRARY),
"scenarios": [
{
"index": i,
"claim_id": case.claim_id,
"claim_type": case.claim_type,
"complexity": case.complexity,
"amount": case.claim_amount,
"is_fraud": case.is_fraud,
}
for i, case in enumerate(CASE_LIBRARY)
],
}
@app.get("/health")
async def health_probe() -> dict[str, str]:
"""Liveness probe used by Spaces, monitors, and the dashboard."""
return {"status": "healthy", "environment": "claimsense"}
# ---------------------------------------------------------------------------
# Local dev entrypoint (``python space_app.py``)
# ---------------------------------------------------------------------------
if __name__ == "__main__":
import uvicorn
port = int(os.environ.get("PORT", 7860))
uvicorn.run(app, host="0.0.0.0", port=port)