Souravdanyal commited on
Commit
ceba2ab
Β·
1 Parent(s): e1f5917

fully tested

Browse files
run.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+
4
+ # Add project root and OpenEnv to path
5
+ BASE = os.path.dirname(os.path.abspath(__file__))
6
+ sys.path.insert(0, BASE)
7
+ sys.path.insert(0, os.path.join(BASE, "OpenEnv"))
8
+ sys.path.insert(0, os.path.join(BASE, "OpenEnv", "src"))
9
+
10
+ import uvicorn
11
+
12
+ if __name__ == "__main__":
13
+ uvicorn.run("server.app:app", host="127.0.0.1", port=7860, reload=True)
server/__pycache__/app.cpython-310.pyc CHANGED
Binary files a/server/__pycache__/app.cpython-310.pyc and b/server/__pycache__/app.cpython-310.pyc differ
 
server/app.py CHANGED
@@ -1,16 +1,13 @@
1
  # server/app.py
2
- # FastAPI server exposing the OpenEnv standard endpoints.
3
- # Port 7860 required for Hugging Face Spaces.
4
-
5
  from fastapi import FastAPI, HTTPException
6
  from fastapi.middleware.cors import CORSMiddleware
 
7
  from typing import Optional
8
  from pydantic import BaseModel
 
9
 
10
  from server.environment import CodeDebugEnvironment
11
  from models import DebugAction, DebugObservation, DebugState
12
- from fastapi.responses import HTMLResponse
13
- import os
14
 
15
  app = FastAPI(
16
  title="Code Debug Environment",
@@ -28,20 +25,24 @@ app.add_middleware(
28
  allow_headers=["*"],
29
  )
30
 
31
- # One global environment instance (single session)
32
- # For concurrent sessions, instantiate per-request with a session dict
33
  env = CodeDebugEnvironment()
 
 
34
  @app.get("/", response_class=HTMLResponse)
35
  async def root():
 
36
  html_path = os.path.join(os.path.dirname(__file__), "static", "index.html")
37
- with open(html_path, "r") as f:
38
  return f.read()
39
 
40
 
41
- # ─── Request Models ─────────────────────────────────────────────────────────
 
 
 
42
 
43
  class ResetRequest(BaseModel):
44
- difficulty: Optional[str] = None # "easy" | "medium" | "hard" | None (random)
45
 
46
 
47
  class StepRequest(BaseModel):
@@ -49,53 +50,27 @@ class StepRequest(BaseModel):
49
  explanation: Optional[str] = None
50
 
51
 
52
- # ─── Response wrapper matching OpenEnv StepResult shape ──────────────────────
53
-
54
  class StepResponse(BaseModel):
55
  observation: dict
56
  reward: float
57
  done: bool
58
 
59
 
60
- # ─── Endpoints ───────────────────────────────────────────────────────────────
61
-
62
- @app.get("/health")
63
- async def health():
64
- """Health check endpoint β€” must return 200 for submission validation."""
65
- return {"status": "ok", "environment": "code-debug-env", "version": "1.0.0"}
66
-
67
-
68
  @app.post("/reset")
69
  async def reset(request: ResetRequest = ResetRequest()) -> dict:
70
- """
71
- Reset the environment to start a new episode.
72
- Optionally pass difficulty: 'easy' | 'medium' | 'hard'
73
- """
74
  try:
75
  observation = env.reset(difficulty=request.difficulty)
76
- return {
77
- "observation": observation.model_dump(),
78
- "reward": 0.0,
79
- "done": False,
80
- }
81
  except Exception as e:
82
  raise HTTPException(status_code=500, detail=f"Reset failed: {str(e)}")
83
 
84
 
85
  @app.post("/step")
86
  async def step(request: StepRequest) -> StepResponse:
87
- """
88
- Submit a code fix (and optional explanation for hard tasks).
89
- Returns observation with reward (0.0–1.0), feedback, and done flag.
90
- """
91
  if not request.fixed_code or not request.fixed_code.strip():
92
  raise HTTPException(status_code=400, detail="fixed_code must not be empty.")
93
-
94
  try:
95
- action = DebugAction(
96
- fixed_code=request.fixed_code,
97
- explanation=request.explanation,
98
- )
99
  observation = env.step(action)
100
  return StepResponse(
101
  observation=observation.model_dump(),
@@ -108,17 +83,14 @@ async def step(request: StepRequest) -> StepResponse:
108
 
109
  @app.get("/state")
110
  async def state() -> dict:
111
- """Return the current episode state."""
112
  try:
113
- s = env.state
114
- return s.model_dump()
115
  except Exception as e:
116
  raise HTTPException(status_code=500, detail=f"State failed: {str(e)}")
117
 
118
 
119
  @app.get("/tasks")
120
  async def list_tasks() -> dict:
121
- """List available task IDs per difficulty (for inspection)."""
122
  from server.tasks.task_easy import EASY_TASKS
123
  from server.tasks.task_medium import MEDIUM_TASKS
124
  from server.tasks.task_hard import HARD_TASKS
@@ -129,16 +101,10 @@ async def list_tasks() -> dict:
129
  "total": len(EASY_TASKS) + len(MEDIUM_TASKS) + len(HARD_TASKS),
130
  }
131
 
132
- @app.get("/")
133
- async def root():
134
- return {
135
- "name": "Code Debug Environment",
136
- "version": "1.0.0",
137
- "endpoints": {
138
- "health": "/health",
139
- "reset": "/reset",
140
- "step": "/step",
141
- "state": "/state",
142
- "docs": "/docs"
143
- }
144
- }
 
1
  # server/app.py
 
 
 
2
  from fastapi import FastAPI, HTTPException
3
  from fastapi.middleware.cors import CORSMiddleware
4
+ from fastapi.responses import HTMLResponse
5
  from typing import Optional
6
  from pydantic import BaseModel
7
+ import os
8
 
9
  from server.environment import CodeDebugEnvironment
10
  from models import DebugAction, DebugObservation, DebugState
 
 
11
 
12
  app = FastAPI(
13
  title="Code Debug Environment",
 
25
  allow_headers=["*"],
26
  )
27
 
 
 
28
  env = CodeDebugEnvironment()
29
+
30
+
31
  @app.get("/", response_class=HTMLResponse)
32
  async def root():
33
+ """Homepage with live tester UI."""
34
  html_path = os.path.join(os.path.dirname(__file__), "static", "index.html")
35
+ with open(html_path, "r", encoding="utf-8") as f:
36
  return f.read()
37
 
38
 
39
+ @app.get("/health")
40
+ async def health():
41
+ return {"status": "ok", "environment": "code-debug-env", "version": "1.0.0"}
42
+
43
 
44
  class ResetRequest(BaseModel):
45
+ difficulty: Optional[str] = None
46
 
47
 
48
  class StepRequest(BaseModel):
 
50
  explanation: Optional[str] = None
51
 
52
 
 
 
53
  class StepResponse(BaseModel):
54
  observation: dict
55
  reward: float
56
  done: bool
57
 
58
 
 
 
 
 
 
 
 
 
59
  @app.post("/reset")
60
  async def reset(request: ResetRequest = ResetRequest()) -> dict:
 
 
 
 
61
  try:
62
  observation = env.reset(difficulty=request.difficulty)
63
+ return {"observation": observation.model_dump(), "reward": 0.0, "done": False}
 
 
 
 
64
  except Exception as e:
65
  raise HTTPException(status_code=500, detail=f"Reset failed: {str(e)}")
66
 
67
 
68
  @app.post("/step")
69
  async def step(request: StepRequest) -> StepResponse:
 
 
 
 
70
  if not request.fixed_code or not request.fixed_code.strip():
71
  raise HTTPException(status_code=400, detail="fixed_code must not be empty.")
 
72
  try:
73
+ action = DebugAction(fixed_code=request.fixed_code, explanation=request.explanation)
 
 
 
74
  observation = env.step(action)
75
  return StepResponse(
76
  observation=observation.model_dump(),
 
83
 
84
  @app.get("/state")
85
  async def state() -> dict:
 
86
  try:
87
+ return env.state.model_dump()
 
88
  except Exception as e:
89
  raise HTTPException(status_code=500, detail=f"State failed: {str(e)}")
90
 
91
 
92
  @app.get("/tasks")
93
  async def list_tasks() -> dict:
 
94
  from server.tasks.task_easy import EASY_TASKS
95
  from server.tasks.task_medium import MEDIUM_TASKS
96
  from server.tasks.task_hard import HARD_TASKS
 
101
  "total": len(EASY_TASKS) + len(MEDIUM_TASKS) + len(HARD_TASKS),
102
  }
103
 
104
+
105
+ # ─── Run directly with: python server/app.py ─────────────────────────────────
106
+ if __name__ == "__main__":
107
+ import sys
108
+ import uvicorn
109
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
110
+ uvicorn.run("server.app:app", host="127.0.0.1", port=7860, reload=True)
 
 
 
 
 
 
server/graders/__pycache__/grader_easy.cpython-310.pyc CHANGED
Binary files a/server/graders/__pycache__/grader_easy.cpython-310.pyc and b/server/graders/__pycache__/grader_easy.cpython-310.pyc differ
 
server/graders/grader_easy.py CHANGED
@@ -28,8 +28,16 @@ def _run_code_safely(code: str, func_name: str, test_input):
28
  func = funcs[0]
29
 
30
  try:
31
- if isinstance(test_input, list):
 
32
  result = func(*test_input)
 
 
 
 
 
 
 
33
  else:
34
  result = func(test_input)
35
  return result, None
@@ -71,14 +79,14 @@ def grade_easy(fixed_code: str, task: dict) -> Tuple[float, int, int, str, List[
71
 
72
  if error:
73
  results.append({"test_id": i + 1, "passed": False, "expected": str(expected), "got": f"ERROR: {error}"})
74
- feedback_lines.append(f"Test {i+1}: ❌ Error β€” {error}")
75
  elif got == expected:
76
  passed += 1
77
  results.append({"test_id": i + 1, "passed": True, "expected": str(expected), "got": str(got)})
78
- feedback_lines.append(f"Test {i+1}: βœ… Passed β€” got {got!r}")
79
  else:
80
  results.append({"test_id": i + 1, "passed": False, "expected": str(expected), "got": str(got)})
81
- feedback_lines.append(f"Test {i+1}: ❌ Failed β€” expected {expected!r}, got {got!r}")
82
 
83
  reward = round(passed / total, 2)
84
  feedback = "\n".join(feedback_lines)
@@ -87,4 +95,4 @@ def grade_easy(fixed_code: str, task: dict) -> Tuple[float, int, int, str, List[
87
  else:
88
  feedback += f"\n{passed}/{total} tests passed. Review the failing cases."
89
 
90
- return reward, passed, total, feedback, results
 
28
  func = funcs[0]
29
 
30
  try:
31
+ if isinstance(test_input, list) and len(test_input) > 0 and isinstance(test_input[0], list):
32
+ # List of lists = multiple arguments e.g. [[1,2,3], 2] β†’ func([1,2,3], 2)
33
  result = func(*test_input)
34
+ elif isinstance(test_input, list):
35
+ # Try passing as single list argument first
36
+ try:
37
+ result = func(test_input)
38
+ except TypeError:
39
+ # Fallback: unpack as multiple args
40
+ result = func(*test_input)
41
  else:
42
  result = func(test_input)
43
  return result, None
 
79
 
80
  if error:
81
  results.append({"test_id": i + 1, "passed": False, "expected": str(expected), "got": f"ERROR: {error}"})
82
+ feedback_lines.append(f"Test {i+1}: ❌ Error\n Input : {inp!r}\n Expected : {expected!r}\n Error : {error}")
83
  elif got == expected:
84
  passed += 1
85
  results.append({"test_id": i + 1, "passed": True, "expected": str(expected), "got": str(got)})
86
+ feedback_lines.append(f"Test {i+1}: βœ… Passed\n Input : {inp!r}\n Expected : {expected!r}\n Got : {got!r}")
87
  else:
88
  results.append({"test_id": i + 1, "passed": False, "expected": str(expected), "got": str(got)})
89
+ feedback_lines.append(f"Test {i+1}: ❌ Failed\n Input : {inp!r}\n Expected : {expected!r}\n Got : {got!r}")
90
 
91
  reward = round(passed / total, 2)
92
  feedback = "\n".join(feedback_lines)
 
95
  else:
96
  feedback += f"\n{passed}/{total} tests passed. Review the failing cases."
97
 
98
+ return reward, passed, total, feedback, results