Spaces:
Sleeping
Sleeping
File size: 5,057 Bytes
64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea a0fe78f 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 a0fe78f 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea 0a88609 64305ea | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | """
app.py β OpenEnv-compliant FastAPI server for HuggingFace Space deployment.
Implements the openenv-core HTTPEnvServer contract exactly:
POST /reset body: {} or {"task": "<id>"}
β {"observation": {...}, "reward": null, "done": false}
POST /step body: {"action": {"type":..,"target":..,"payload":..}}
β {"observation": {...}, "reward": float, "done": bool}
GET /state β {"episode_id": null, "step_count": int}
GET /health β {"status": "healthy"}
GET /tasks β {"tasks": [...]}
HF Space URL: https://revrse-openenv-redteaming.hf.space
"""
from __future__ import annotations
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from typing import Any, Dict, Optional
from fastapi import Body, FastAPI, HTTPException
from env import VulnEnv
# ββ App βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
app = FastAPI(
title="OpenEnv Red-Team Environment",
description="Multi-step red-team tasks: SQL Injection, Spear-Phishing, Cloud Identity, AI Exploitation",
version="1.0.0",
)
_env = VulnEnv()
_current_obs: Optional[Dict] = None
_step_count: int = 0
_current_task: str = _env.task_ids[0] # default task
# ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def _make_observation(state: Dict) -> Dict:
"""Strip reward/done from state dict and return as observation payload."""
return {
"task": state.get("task", ""),
"code_context": state.get("code_context", ""),
"recent_action": state.get("recent_action"),
"recent_output": str(state.get("recent_output", "") or ""),
"signals": state.get("signals", {}),
"step_count": state.get("step_count", 0),
}
# ββ Endpoints (openenv-core HTTPEnvServer contract) βββββββββββββββββββββββββββ
@app.get("/health")
def health():
"""Liveness probe β returns 200 {"status": "healthy"}."""
return {"status": "healthy", "tasks": _env.task_ids}
@app.get("/tasks")
def list_tasks():
"""Enumerate available task IDs."""
return {"tasks": _env.task_ids}
@app.post("/reset")
def reset(request: Dict[str, Any] = Body(default={})):
"""
Reset the environment.
Accepts:
{} β resets to default task (sql_injection)
{"task": "auth_bypass"} β resets to specified task
Returns openenv-core format:
{"observation": {...}, "reward": null, "done": false}
"""
global _current_obs, _step_count, _current_task
task = request.get("task", _current_task)
if task not in _env.task_ids:
task = _env.task_ids[0]
_current_task = task
try:
state = _env.reset(task)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
_current_obs = state
_step_count = 0
return {
"observation": _make_observation(state),
"reward": None,
"done": False,
}
@app.post("/step")
def step(request: Dict[str, Any] = Body(...)):
"""
Apply a structured action.
Accepts:
{"action": {"type": "select", "target": "<tool_name>", "payload": ""}}
Returns openenv-core format:
{"observation": {...}, "reward": float, "done": bool}
"""
global _current_obs, _step_count
if _current_obs is None:
raise HTTPException(
status_code=400,
detail="Not initialised. Call POST /reset first."
)
action = request.get("action", {})
if not action:
raise HTTPException(status_code=400, detail="'action' field required.")
try:
state, reward, done, _info = _env.step(action)
except RuntimeError as exc:
raise HTTPException(status_code=400, detail=str(exc))
_current_obs = state
_step_count = state.get("step_count", _step_count + 1)
return {
"observation": _make_observation(state),
"reward": reward,
"done": done,
}
@app.get("/state")
def get_state():
"""
Return current environment state (read-only).
Matches openenv-core State dataclass: {episode_id, step_count}.
"""
return {
"episode_id": None,
"step_count": _step_count,
"task": _current_task,
}
# ββ Entry point βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if __name__ == "__main__":
import uvicorn
uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=False)
|