| """Main FastAPI application for Code Security Review. |
| |
| Exposes RESTful endpoints conforming to standard OpenEnv compliance specifications |
| dictating interactions for agent evaluation. |
| """ |
|
|
| import os |
| import uvicorn |
| from typing import List, Optional |
| from fastapi import FastAPI, HTTPException, Query, status |
| from fastapi.middleware.cors import CORSMiddleware |
|
|
| from server.models import CodeReviewAction, StepResult, ResetResponse, StateResponse, TaskInfo |
| from server.tasks import TASKS |
| from server.environment import CodeSecurityEnv |
|
|
| app = FastAPI( |
| title="Code Security Review — OpenEnv", |
| description="An RL environment for training AI agents to perform code security review.", |
| version="1.0.0", |
| ) |
|
|
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| env = CodeSecurityEnv() |
|
|
|
|
| @app.get("/") |
| def health() -> dict: |
| """Health check endpoint.""" |
| return { |
| "status": "ok", |
| "project": "Code Security Review - OpenEnv", |
| "version": "1.0.0", |
| "organization": "Inmodel Labs", |
| } |
|
|
|
|
| @app.get("/tasks", response_model=List[TaskInfo]) |
| def list_tasks() -> List[TaskInfo]: |
| """List all available tasks.""" |
| return [ |
| TaskInfo( |
| id=t["id"], |
| language=t["language"], |
| bug_class=t["bug_class"], |
| difficulty=t["difficulty"], |
| ) |
| for t in TASKS.values() |
| ] |
|
|
|
|
| @app.post("/reset", response_model=ResetResponse) |
| def reset( |
| task_id: str = Query(default="python-off-by-one", description="Task ID to reset to"), |
| seed: Optional[int] = Query(default=None, description="Optional seed for reproducibility") |
| ) -> ResetResponse: |
| """Reset the environment and return the first observation.""" |
| if task_id not in TASKS: |
| raise HTTPException( |
| status_code=status.HTTP_404_NOT_FOUND, |
| detail=f"Task '{task_id}' not found." |
| ) |
| |
| try: |
| obs = env.reset(task_id=task_id, seed=seed) |
| return ResetResponse(observation=obs) |
| except Exception as e: |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail=f"System breakdown during environment reset: {e}" |
| ) |
|
|
|
|
| @app.post("/step", response_model=StepResult) |
| def step(action: CodeReviewAction) -> StepResult: |
| """Submit a code review action and receive a reward signal.""" |
| try: |
| return env.step(action) |
| except Exception as e: |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail=f"Error executing agent action logic: {e}" |
| ) |
|
|
|
|
| @app.get("/state", response_model=StateResponse) |
| def state() -> StateResponse: |
| """Return the current environment state.""" |
| try: |
| return env.state() |
| except Exception as e: |
| raise HTTPException( |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, |
| detail=f"Error analyzing global runtime state tracking: {e}" |
| ) |
|
|
|
|
| def main() -> None: |
| """Run the environment ASGI server natively.""" |
| port_default = os.environ.get("PORT", "8000") |
| try: |
| port = int(port_default) |
| except ValueError: |
| port = 8000 |
|
|
| uvicorn.run( |
| "server.app:app", |
| host="0.0.0.0", |
| port=port, |
| reload=False, |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|