SimranShaikh commited on
Commit
998a566
Β·
verified Β·
1 Parent(s): f6d5289
Files changed (1) hide show
  1. app.py +95 -0
app.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FastAPI server exposing the OpenEnv-compatible HTTP API.
3
+ Deployed as a Hugging Face Space on port 7860.
4
+
5
+ Endpoints
6
+ ─────────
7
+ GET /health β€” liveness probe
8
+ GET /tasks β€” list available tasks
9
+ POST /reset β€” start new episode { "task_id": "easy_syntax" }
10
+ POST /step β€” take action CodeReviewAction JSON body
11
+ GET /state β€” current state snapshot
12
+ """
13
+ import os
14
+ from typing import Optional
15
+
16
+ import uvicorn
17
+ from fastapi import FastAPI, HTTPException, Query
18
+ from fastapi.middleware.cors import CORSMiddleware
19
+
20
+ from environment.env import CodeReviewEnv
21
+ from environment.models import CodeReviewAction
22
+
23
+ app = FastAPI(
24
+ title="CodeReview-Env",
25
+ description=(
26
+ "An OpenEnv reinforcement-learning environment where AI agents learn to "
27
+ "review code for syntax errors, logic bugs, and security vulnerabilities."
28
+ ),
29
+ version="1.0.0",
30
+ )
31
+
32
+ app.add_middleware(
33
+ CORSMiddleware,
34
+ allow_origins=["*"],
35
+ allow_methods=["*"],
36
+ allow_headers=["*"],
37
+ )
38
+
39
+ # Single global environment instance (one session per container)
40
+ _env = CodeReviewEnv()
41
+
42
+
43
+ # ── endpoints ────────────────────────────────────────────────────────────────
44
+
45
+ @app.get("/health")
46
+ async def health():
47
+ return {"status": "ok", "env": "CodeReview-Env", "version": "1.0.0"}
48
+
49
+
50
+ @app.get("/tasks")
51
+ async def list_tasks():
52
+ return {"tasks": _env.list_tasks()}
53
+
54
+
55
+ @app.post("/reset")
56
+ async def reset(task_id: Optional[str] = Query(default="easy_syntax")):
57
+ """
58
+ Reset the environment and start a new episode.
59
+ task_id: one of easy_syntax | medium_logic | hard_security
60
+ """
61
+ try:
62
+ result = _env.reset(task_id=task_id)
63
+ return result.model_dump()
64
+ except ValueError as e:
65
+ raise HTTPException(status_code=400, detail=str(e))
66
+
67
+
68
+ @app.post("/step")
69
+ async def step(action: CodeReviewAction):
70
+ """
71
+ Submit a code-review action.
72
+ Returns observation, reward (0.0–1.0), done flag, and grader feedback.
73
+ """
74
+ if not _env.is_initialized:
75
+ raise HTTPException(status_code=400, detail="Call POST /reset first.")
76
+ try:
77
+ result = _env.step(action)
78
+ return result.model_dump()
79
+ except RuntimeError as e:
80
+ raise HTTPException(status_code=400, detail=str(e))
81
+
82
+
83
+ @app.get("/state")
84
+ async def state():
85
+ """Return a snapshot of the current episode state."""
86
+ if not _env.is_initialized:
87
+ raise HTTPException(status_code=400, detail="Call POST /reset first.")
88
+ return _env.get_state().model_dump()
89
+
90
+
91
+ # ── entrypoint ───────────────────────────────────────────────────────────────
92
+
93
+ if __name__ == "__main__":
94
+ port = int(os.environ.get("PORT", 7860))
95
+ uvicorn.run("app:app", host="0.0.0.0", port=port, reload=False)