affu143 commited on
Commit
6079dab
Β·
1 Parent(s): 13c6248
inference.py CHANGED
@@ -199,6 +199,13 @@ async def main():
199
  log_start(task=current_task, env=benchmark, model=model_name)
200
 
201
  result = step_result
 
 
 
 
 
 
 
202
 
203
  except Exception as e:
204
  print(f"[DEBUG] Fatal error: {e}", flush=True)
 
199
  log_start(task=current_task, env=benchmark, model=model_name)
200
 
201
  result = step_result
202
+ else:
203
+ # Loop completed without break β€” emit [END] for the last active task
204
+ if task_rewards:
205
+ task_score = clamp(sum(task_rewards) / len(task_rewards))
206
+ else:
207
+ task_score = 0.01
208
+ log_end(task_score >= 0.5, task_step, task_score, task_rewards)
209
 
210
  except Exception as e:
211
  print(f"[DEBUG] Fatal error: {e}", flush=True)
server/app.py CHANGED
@@ -1,13 +1,9 @@
1
  """FastAPI application for ConfigDebugEnv.
2
 
3
- Uses OpenEnv's create_fastapi_app() for standard framework compatibility
4
- (WebSocket sessions, standard endpoints, grader discovery).
5
  """
6
- import json
7
  import gradio as gr
8
- from fastapi import Request
9
- from starlette.middleware.base import BaseHTTPMiddleware
10
- from starlette.responses import JSONResponse
11
 
12
  from openenv.core.env_server import create_fastapi_app
13
  from server.models import ConfigDebugAction, ConfigDebugObservation, ConfigDebugState
@@ -15,52 +11,13 @@ from server.config_debug_environment import ConfigDebugEnvironment
15
  from server.tasks.task_registry import get_task, TASK_ORDER
16
 
17
  # ---- Create the standard OpenEnv FastAPI app ----
 
18
  app = create_fastapi_app(
19
  ConfigDebugEnvironment,
20
  ConfigDebugAction,
21
  ConfigDebugObservation,
22
  )
23
 
24
- # ---- Remove default routes we need to override ----
25
- for i, route in enumerate(app.router.routes):
26
- if hasattr(route, "path") and route.path == "/reset":
27
- app.router.routes.pop(i)
28
- print("[APP_INIT] Removed default /reset route for schema fix")
29
- break
30
-
31
- for i, route in enumerate(app.router.routes):
32
- if hasattr(route, "path") and route.path == "/metadata":
33
- app.router.routes.pop(i)
34
- print("[APP_INIT] Removed default /metadata route for override")
35
- break
36
-
37
- # ---- Middleware to fix /reset response schema ----
38
- class ResetSchemaFixMiddleware(BaseHTTPMiddleware):
39
- async def dispatch(self, request, call_next):
40
- response = await call_next(request)
41
- if request.url.path == "/reset" and request.method == "POST":
42
- if response.status_code == 200:
43
- try:
44
- body = b""
45
- async for chunk in response.body_iterator:
46
- body += chunk
47
- data = json.loads(body)
48
- if isinstance(data, dict) and "observation" in data:
49
- fixed_data = {
50
- "observation": data["observation"],
51
- "done": data.get("done", False),
52
- "reward": 0.0,
53
- "metadata": data.get("metadata", {}),
54
- "info": {}
55
- }
56
- print("[MIDDLEWARE] Fixed /reset response schema - added base fields")
57
- return JSONResponse(fixed_data, status_code=200)
58
- except Exception as e:
59
- print(f"[MIDDLEWARE] Error fixing reset response: {e}")
60
- return response
61
-
62
- app.add_middleware(ResetSchemaFixMiddleware)
63
-
64
  # ---- Startup Diagnostics ----
65
  print("[APP_INIT] ConfigDebugEnvironment initialization started")
66
  print(f"[APP_INIT] Loaded {len(TASK_ORDER)} tasks: {TASK_ORDER}")
@@ -72,108 +29,13 @@ for task_id in TASK_ORDER:
72
  print(f"[APP_INIT] ERROR loading task '{task_id}': {str(e)}")
73
 
74
 
75
- # ---- Custom endpoints ----
76
 
77
  @app.get("/info")
78
  def info():
79
  return {"name": "ConfigDebugEnv", "version": "1.0.0", "status": "running"}
80
 
81
 
82
- @app.get("/health")
83
- def health():
84
- return {"status": "healthy"}
85
-
86
-
87
- @app.get("/metadata")
88
- def metadata():
89
- """Metadata endpoint with grader paths for validator discovery."""
90
- print("[VALIDATOR] GET /metadata called")
91
-
92
- task_grader_map = {
93
- "task1_json": "Task1Grader",
94
- "task2_yaml": "Task2Grader",
95
- "task3_dockerfile": "Task3Grader",
96
- "task4_compose": "Task4Grader",
97
- "task5_k8s": "Task5Grader",
98
- "task6_github_actions": "Task6Grader",
99
- "task7_nginx": "Task7Grader",
100
- }
101
-
102
- return {
103
- "name": "ConfigDebugEnvironment",
104
- "description": "An environment for training AI agents to debug broken configuration files",
105
- "version": "1.0.0",
106
- "tasks": [
107
- {
108
- "id": tid,
109
- "has_grader": True,
110
- "grader": f"server.graders.grader_api:{task_grader_map[tid]}",
111
- }
112
- for tid in TASK_ORDER
113
- ],
114
- }
115
-
116
-
117
- @app.post("/reset")
118
- async def reset_env(request: Request):
119
- """Override /reset endpoint to return correct OpenEnv contract schema."""
120
- print("[VALIDATOR] POST /reset called - CUSTOM OVERRIDE")
121
- try:
122
- env = ConfigDebugEnvironment()
123
- observation = env.reset()
124
- fixed_response = {
125
- "observation": observation.model_dump(),
126
- "done": False,
127
- "reward": 0.0,
128
- "metadata": {},
129
- "info": {}
130
- }
131
- print("[VALIDATOR] Reset response formatted with base fields")
132
- return fixed_response
133
- except Exception as e:
134
- print(f"[VALIDATOR] Error in custom reset: {e}")
135
- raise
136
-
137
-
138
- @app.post("/grader")
139
- async def grader_endpoint(request: Request):
140
- """Score a submitted config for a specific task without a full episode.
141
- The validator calls this to verify each task has a working grader
142
- with scores strictly between 0 and 1."""
143
- print("[VALIDATOR] POST /grader called")
144
- try:
145
- body = await request.json()
146
- task_id = body.get("task_id", TASK_ORDER[0])
147
- submitted_config = body.get("submitted_config",
148
- body.get("action", {}).get("fixed_config", "{}"))
149
-
150
- from server.graders.grader_api import (
151
- Task1Grader, Task2Grader, Task3Grader,
152
- Task4Grader, Task5Grader, Task6Grader, Task7Grader,
153
- )
154
-
155
- grader_map = {
156
- "task1_json": Task1Grader(),
157
- "task2_yaml": Task2Grader(),
158
- "task3_dockerfile": Task3Grader(),
159
- "task4_compose": Task4Grader(),
160
- "task5_k8s": Task5Grader(),
161
- "task6_github_actions": Task6Grader(),
162
- "task7_nginx": Task7Grader(),
163
- }
164
-
165
- grader = grader_map.get(task_id)
166
- if grader is None:
167
- return {"error": f"Unknown task_id: {task_id}", "score": 0.01}
168
-
169
- score = grader.grade(submitted_config)
170
- print(f"[GRADER] task={task_id} score={score}")
171
- return {"task_id": task_id, "score": score, "has_grader": True}
172
- except Exception as e:
173
- print(f"[GRADER] Error: {e}")
174
- return {"error": str(e), "score": 0.01}
175
-
176
-
177
  @app.get("/tasks")
178
  def tasks():
179
  task_grader_map = {
@@ -203,15 +65,6 @@ def tasks():
203
  }
204
 
205
 
206
- @app.get("/schema")
207
- def schema():
208
- return {
209
- "action": ConfigDebugAction.model_json_schema(),
210
- "observation": ConfigDebugObservation.model_json_schema(),
211
- "state": ConfigDebugState.model_json_schema(),
212
- }
213
-
214
-
215
  # ---- Gradio Web UI ----
216
 
217
  _ui_env = ConfigDebugEnvironment()
 
1
  """FastAPI application for ConfigDebugEnv.
2
 
3
+ Uses OpenEnv's create_fastapi_app() for standard framework compatibility.
4
+ The framework handles /reset, /step, /state, /health, /schema, /metadata, /ws.
5
  """
 
6
  import gradio as gr
 
 
 
7
 
8
  from openenv.core.env_server import create_fastapi_app
9
  from server.models import ConfigDebugAction, ConfigDebugObservation, ConfigDebugState
 
11
  from server.tasks.task_registry import get_task, TASK_ORDER
12
 
13
  # ---- Create the standard OpenEnv FastAPI app ----
14
+ # The framework registers: /reset, /step, /state, /health, /schema, /metadata, /ws
15
  app = create_fastapi_app(
16
  ConfigDebugEnvironment,
17
  ConfigDebugAction,
18
  ConfigDebugObservation,
19
  )
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  # ---- Startup Diagnostics ----
22
  print("[APP_INIT] ConfigDebugEnvironment initialization started")
23
  print(f"[APP_INIT] Loaded {len(TASK_ORDER)} tasks: {TASK_ORDER}")
 
29
  print(f"[APP_INIT] ERROR loading task '{task_id}': {str(e)}")
30
 
31
 
32
+ # ---- Custom endpoints (non-conflicting with framework) ----
33
 
34
  @app.get("/info")
35
  def info():
36
  return {"name": "ConfigDebugEnv", "version": "1.0.0", "status": "running"}
37
 
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  @app.get("/tasks")
40
  def tasks():
41
  task_grader_map = {
 
65
  }
66
 
67
 
 
 
 
 
 
 
 
 
 
68
  # ---- Gradio Web UI ----
69
 
70
  _ui_env = ConfigDebugEnvironment()
server/config_debug_environment.py CHANGED
@@ -23,6 +23,7 @@ class ConfigDebugEnvironment(Environment):
23
  SUPPORTS_CONCURRENT_SESSIONS = True
24
 
25
  def __init__(self):
 
26
  self._init_episode()
27
 
28
  def _init_episode(self):
@@ -33,7 +34,7 @@ class ConfigDebugEnvironment(Environment):
33
  self._done = False
34
  self.tasks_completed: list = []
35
  self.bugs_found_so_far = 0
36
- self.previous_reward = 0.0
37
  self.current_error_message: Optional[str] = None
38
  self.current_broken_config: Optional[str] = None
39
  self._episode_id = str(uuid4())
@@ -164,7 +165,7 @@ class ConfigDebugEnvironment(Environment):
164
  difficulty=task.difficulty,
165
  num_bugs=task.num_bugs,
166
  bugs_found_so_far=self.bugs_found_so_far,
167
- previous_reward=self.previous_reward,
168
  done=self._done,
169
  reward=self.previous_reward,
170
  )
 
23
  SUPPORTS_CONCURRENT_SESSIONS = True
24
 
25
  def __init__(self):
26
+ super().__init__()
27
  self._init_episode()
28
 
29
  def _init_episode(self):
 
34
  self._done = False
35
  self.tasks_completed: list = []
36
  self.bugs_found_so_far = 0
37
+ self.previous_reward = None
38
  self.current_error_message: Optional[str] = None
39
  self.current_broken_config: Optional[str] = None
40
  self._episode_id = str(uuid4())
 
165
  difficulty=task.difficulty,
166
  num_bugs=task.num_bugs,
167
  bugs_found_so_far=self.bugs_found_so_far,
168
+ previous_reward=self.previous_reward if self.previous_reward is not None else 0.01,
169
  done=self._done,
170
  reward=self.previous_reward,
171
  )
test_env.py DELETED
@@ -1,21 +0,0 @@
1
- from server.config_debug_environment import ConfigDebugEnvironment
2
- from server.models import ConfigDebugAction
3
- from server.tasks.task_registry import get_task
4
-
5
- env = ConfigDebugEnvironment()
6
-
7
- # ONLY ONE RESET
8
- obs = env.reset()
9
-
10
- for i in range(3):
11
- print(f"\n--- Step {i+1} ---")
12
-
13
- task = get_task(obs.task_id)
14
- correct_output = task.ground_truth
15
-
16
- action = ConfigDebugAction(fixed_config=correct_output)
17
- obs = env.step(action)
18
-
19
- print("Task ID:", obs.task_id)
20
- print("Reward:", obs.reward)
21
- print("Done:", obs.done)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_grader_audit.py DELETED
@@ -1,95 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Runtime audit of all graders - test actual outputs without validator.
4
- Tests what the validator will actually receive.
5
- """
6
-
7
- import sys
8
- from server.graders.grader_api import (
9
- grade_task1, grade_task2, grade_task3, grade_task4,
10
- grade_task5, grade_task6, grade_task7,
11
- )
12
- from server.tasks import (
13
- task1_json, task2_yaml, task3_dockerfile,
14
- task4_compose, task5_k8s, task6_github_actions, task7_nginx
15
- )
16
-
17
- # Test data
18
- TESTS = [
19
- (1, grade_task1, task1_json.BROKEN_CONFIG, "JSON"),
20
- (2, grade_task2, task2_yaml.BROKEN_CONFIG, "YAML"),
21
- (3, grade_task3, task3_dockerfile.BROKEN_CONFIG, "Dockerfile"),
22
- (4, grade_task4, task4_compose.BROKEN_CONFIG, "Compose"),
23
- (5, grade_task5, task5_k8s.BROKEN_CONFIG, "K8s"),
24
- (6, grade_task6, task6_github_actions.BROKEN_CONFIG, "GitHub Actions"),
25
- (7, grade_task7, task7_nginx.BROKEN_CONFIG, "Nginx"),
26
- ]
27
-
28
- print("=" * 80)
29
- print("GRADER RUNTIME AUDIT - ALL GRADERS WITH BROKEN CONFIGS")
30
- print("Validator Contract: All graders must return FLOAT in (0, 1) ONLY")
31
- print("=" * 80)
32
- print()
33
-
34
- failures = []
35
- all_valid = True
36
-
37
- for task_num, grader_func, broken_config, name in TESTS:
38
- task_id = f"task{task_num}_{name.lower().replace(' ', '_')}"
39
-
40
- print(f"Testing Task {task_num} ({name})...")
41
- print(f" Grader: {grader_func.__name__}")
42
-
43
- try:
44
- result = grader_func(broken_config)
45
-
46
- # Analyze output - MUST be float only
47
- result_type = type(result).__name__
48
- print(f" Output type: {result_type}")
49
-
50
- if isinstance(result, float):
51
- reward = result
52
- is_valid = 0 < reward < 1
53
-
54
- print(f" Value: {reward}")
55
- print(f" In bounds (0, 1): {is_valid}")
56
- print(f" Exactly 0.0: {reward == 0.0}")
57
- print(f" Exactly 1.0: {reward == 1.0}")
58
- print(f" NaN check: {reward != reward}")
59
- print(f" Inf check: {abs(reward) > 1e308}")
60
-
61
- if not is_valid:
62
- all_valid = False
63
- failures.append(f"Task {task_num}: Reward {reward} NOT in (0, 1)")
64
- print(f" ❌ INVALID: Reward {reward} is not in (0, 1) range")
65
- else:
66
- print(f" βœ… Valid")
67
- else:
68
- all_valid = False
69
- failures.append(f"Task {task_num}: Expected FLOAT, got {result_type}")
70
- print(f" ❌ INVALID: Expected float, got {result_type}")
71
- print(f" Value: {result}")
72
-
73
- except Exception as e:
74
- all_valid = False
75
- failures.append(f"Task {task_num}: Exception - {type(e).__name__}: {str(e)}")
76
- print(f" ❌ EXCEPTION: {type(e).__name__}: {str(e)}")
77
-
78
- print()
79
-
80
- print("=" * 80)
81
- print("SUMMARY")
82
- print("=" * 80)
83
- print(f"All graders valid: {all_valid}")
84
- print(f"Total tests: {len(TESTS)}")
85
- print(f"Passed: {len(TESTS) - len(failures)}")
86
- print(f"Failed: {len(failures)}")
87
-
88
- if failures:
89
- print("\nFailures:")
90
- for failure in failures:
91
- print(f" - {failure}")
92
- sys.exit(1)
93
- else:
94
- print("\nβœ… All graders return valid FLOATS in (0, 1)!")
95
- sys.exit(0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_k8s_multistep.py DELETED
@@ -1,88 +0,0 @@
1
- from server.graders.k8s_grader import grade_task5
2
-
3
- # Test 1: Only fix replicas
4
- config1 = """apiVersion: apps/v1
5
- kind: Deployment
6
- metadata:
7
- name: my-app
8
- spec:
9
- replicas: 3
10
- selector:
11
- matchLabels:
12
- app: my-app
13
- template:
14
- metadata:
15
- labels:
16
- app: my-app
17
- spec:
18
- containers:
19
- - name: my-container
20
- image: nginx
21
- ports:
22
- - containerPort: "80"
23
- resources:
24
- limits:
25
- cpu: "500"
26
- """
27
-
28
- reward1, msg1, fixed1 = grade_task5(config1)
29
- print(f'Step 1 (replicas fixed): Reward={reward1}, Fixed={fixed1}')
30
- print(f' Error: {msg1}\n')
31
-
32
- # Test 2: Fix replicas + port
33
- config2 = """apiVersion: apps/v1
34
- kind: Deployment
35
- metadata:
36
- name: my-app
37
- spec:
38
- replicas: 3
39
- selector:
40
- matchLabels:
41
- app: my-app
42
- template:
43
- metadata:
44
- labels:
45
- app: my-app
46
- spec:
47
- containers:
48
- - name: my-container
49
- image: nginx
50
- ports:
51
- - containerPort: 80
52
- resources:
53
- limits:
54
- cpu: "500"
55
- """
56
-
57
- reward2, msg2, fixed2 = grade_task5(config2)
58
- print(f'Step 2 (replicas + port fixed): Reward={reward2}, Fixed={fixed2}')
59
- print(f' Error: {msg2}\n')
60
-
61
- # Test 3: Fix everything
62
- config3 = """apiVersion: apps/v1
63
- kind: Deployment
64
- metadata:
65
- name: my-app
66
- spec:
67
- replicas: 3
68
- selector:
69
- matchLabels:
70
- app: my-app
71
- template:
72
- metadata:
73
- labels:
74
- app: my-app
75
- spec:
76
- containers:
77
- - name: my-container
78
- image: nginx
79
- ports:
80
- - containerPort: 80
81
- resources:
82
- limits:
83
- cpu: "500m"
84
- """
85
-
86
- reward3, msg3, fixed3 = grade_task5(config3)
87
- print(f'Step 3 (all fixed): Reward={reward3}, Fixed={fixed3}')
88
- print(f' Message: {msg3}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_nginx_multistep.py DELETED
@@ -1,72 +0,0 @@
1
- from server.graders.nginx_grader import grade_task7
2
-
3
- # Test 1: Only fix syntax (listen and error_log semicolons)
4
- config1 = """events {}
5
-
6
- http {
7
- server {
8
- listen 80;
9
-
10
- location / {
11
- proxy_pass localhost:3000
12
- }
13
-
14
- location /api {
15
- proxy_pass http://localhost:5000
16
- }
17
-
18
- error_log logs/error.log;
19
- }
20
- }"""
21
-
22
- reward1, msg1, fixed1 = grade_task7(config1)
23
- print(f'Step 1 (syntax fixed): Reward={reward1}, Fixed={fixed1}')
24
- print(f' Error: {msg1}\n')
25
-
26
- # Test 2: Fix syntax + proxy_pass (add http://)
27
- config2 = """events {}
28
-
29
- http {
30
- server {
31
- listen 80;
32
-
33
- location / {
34
- proxy_pass http://localhost:3000;
35
- }
36
-
37
- location /api {
38
- proxy_pass http://localhost:5000
39
- }
40
-
41
- error_log logs/error.log;
42
- }
43
- }"""
44
-
45
- reward2, msg2, fixed2 = grade_task7(config2)
46
- print(f'Step 2 (syntax + proxy_pass fixed): Reward={reward2}, Fixed={fixed2}')
47
- print(f' Error: {msg2}\n')
48
-
49
- # Test 3: Fix everything (add /api/ and headers)
50
- config3 = """events {}
51
-
52
- http {
53
- server {
54
- listen 80;
55
-
56
- location / {
57
- proxy_pass http://localhost:3000;
58
- }
59
-
60
- location /api/ {
61
- proxy_pass http://localhost:5000;
62
- proxy_set_header Host $host;
63
- proxy_set_header X-Real-IP $remote_addr;
64
- }
65
-
66
- error_log logs/error.log;
67
- }
68
- }"""
69
-
70
- reward3, msg3, fixed3 = grade_task7(config3)
71
- print(f'Step 3 (all fixed): Reward={reward3}, Fixed={fixed3}')
72
- print(f' Message: {msg3}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_task_graders.py DELETED
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Local Test Script: Verify all tasks/graders meet benchmark quality standards.
4
-
5
- This script runs the mentor's quality checklist:
6
- - Broken config should score ~0.05 reward
7
- - Ground truth should score ~0.95 reward
8
- - bugs_fixed list should reflect actual fixes
9
- - No task should return (reward=0.95, bugs=[]) on broken config (indicates broken grader)
10
- """
11
-
12
- from server.tasks.task_registry import TASK_ORDER, get_task
13
-
14
-
15
- def test_all_tasks():
16
- """Test all tasks in TASK_ORDER."""
17
- print("=" * 80)
18
- print("BENCHMARK QUALITY TEST - ALL TASKS")
19
- print("=" * 80)
20
-
21
- all_passed = True
22
-
23
- for task_id in TASK_ORDER:
24
- print(f"\n[TEST] {task_id}")
25
- print("-" * 80)
26
-
27
- task = get_task(task_id)
28
-
29
- # Test 1: Broken config should score low
30
- broken_reward, broken_msg, broken_bugs = task.grader(task.broken_config)
31
- print(f" BROKEN CONFIG:")
32
- print(f" Reward: {broken_reward:.2f} (expected ~0.05)")
33
- print(f" Bugs Fixed: {len(broken_bugs)} (expected 0-1)")
34
- print(f" Message: {broken_msg[:60]}...")
35
-
36
- broken_ok = 0.01 <= broken_reward <= 0.99 # Must be strictly (0, 1)
37
- if not broken_ok:
38
- print(f" [FAIL] Broken config reward out of valid range (0.01-0.99)!")
39
- all_passed = False
40
- else:
41
- print(f" [PASS]")
42
-
43
- # Test 2: Ground truth should score high
44
- truth_reward, truth_msg, truth_bugs = task.grader(task.ground_truth)
45
- print(f"\n GROUND TRUTH:")
46
- print(f" Reward: {truth_reward:.2f} (expected 0.85-0.99)")
47
- print(f" Bugs Fixed: {len(truth_bugs)} (expected {task.num_bugs})")
48
- print(f" Message: {truth_msg[:60]}...")
49
-
50
- truth_ok = 0.85 <= truth_reward <= 0.99 # Should be in valid high range
51
- if not truth_ok:
52
- print(f" [FAIL] Ground truth reward out of expected range!")
53
- all_passed = False
54
- else:
55
- print(f" [PASS]")
56
-
57
- # Test 3: Check for grader logic issues
58
- if broken_reward >= 0.95 and len(broken_bugs) == 0:
59
- print(f"\n [WARNING] Grader may be broken (high reward on broken config)")
60
- all_passed = False
61
-
62
- if truth_reward < 0.85:
63
- print(f"\n [WARNING] Ground truth not scoring highly enough")
64
- all_passed = False
65
-
66
- # Task metadata
67
- print(f"\n Task Metadata:")
68
- print(f" Difficulty: {task.difficulty}")
69
- print(f" Bugs: {task.num_bugs}")
70
- print(f" Type: {task.file_type}")
71
-
72
- print("\n" + "=" * 80)
73
- if all_passed:
74
- print("[SUCCESS] ALL TESTS PASSED - Ready for resubmission")
75
- else:
76
- print("[FAILED] SOME TESTS FAILED - Review above")
77
- print("=" * 80)
78
-
79
- return all_passed
80
-
81
-
82
- if __name__ == "__main__":
83
- success = test_all_tasks()
84
- exit(0 if success else 1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
test_validator_simulation.py DELETED
@@ -1,88 +0,0 @@
1
- """
2
- Simulates EXACTLY what the Scaler validator does:
3
- 1. Import grader classes from openenv.yaml paths
4
- 2. Instantiate each class
5
- 3. Call .grade(env) or .grade(None)
6
- 4. Check score is strictly in (0.01, 0.90)
7
-
8
- Also tests genuine grading: broken configs get LOW scores, fixed configs get HIGH scores.
9
-
10
- Run: python test_validator_simulation.py
11
- """
12
- import sys
13
- import os
14
-
15
- # Add project root to path
16
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
17
-
18
- def test_grader_classes():
19
- print("=" * 60)
20
- print("VALIDATOR SIMULATION: Testing grader classes")
21
- print("=" * 60)
22
-
23
- from server.graders.grader_api import (
24
- Task1Grader, Task2Grader, Task3Grader,
25
- Task4Grader, Task5Grader, Task6Grader, Task7Grader,
26
- )
27
- from server.config_debug_environment import ConfigDebugEnvironment
28
- from server.tasks.task_registry import get_task, TASK_ORDER
29
-
30
- grader_classes = {
31
- "task1_json": Task1Grader,
32
- "task2_yaml": Task2Grader,
33
- "task3_dockerfile": Task3Grader,
34
- "task4_compose": Task4Grader,
35
- "task5_k8s": Task5Grader,
36
- "task6_github_actions": Task6Grader,
37
- "task7_nginx": Task7Grader,
38
- }
39
-
40
- all_pass = True
41
-
42
- for task_id, GraderClass in grader_classes.items():
43
- grader = GraderClass()
44
- task = get_task(task_id)
45
-
46
- # Test 1: .grade(None) β€” validator may call with None
47
- # Should grade the BROKEN config β†’ low/medium score
48
- score_none = grader.grade(None)
49
- ok_none = 0.0 < score_none < 1.0
50
-
51
- # Test 2: .grade(env) β€” validator passes fresh environment
52
- env = ConfigDebugEnvironment()
53
- env.reset(task_id=task_id)
54
- score_env = grader.grade(env)
55
- ok_env = 0.0 < score_env < 1.0
56
-
57
- # Test 3: .grade(ground_truth_string) β€” direct string (correct answer)
58
- score_gt = grader.grade(task.ground_truth)
59
- ok_gt = 0.0 < score_gt < 1.0
60
-
61
- # Test 4: .grade(broken_config) β€” direct string (broken)
62
- score_broken = grader.grade(task.broken_config)
63
- ok_broken = 0.0 < score_broken < 1.0
64
-
65
- # Verify genuine grading: fixed > broken
66
- genuine = score_gt > score_broken or score_gt == score_broken # fixed should score higher
67
-
68
- status = "PASS" if (ok_none and ok_env and ok_gt and ok_broken) else "FAIL"
69
- if not (ok_none and ok_env and ok_gt and ok_broken):
70
- all_pass = False
71
-
72
- print(f"\n[{status}] {task_id}")
73
- print(f" .grade(None) = {score_none:.4f} {'OK' if ok_none else 'OUT OF RANGE!'}")
74
- print(f" .grade(env) = {score_env:.4f} {'OK' if ok_env else 'OUT OF RANGE!'}")
75
- print(f" .grade(ground_truth) = {score_gt:.4f} (correct answer)")
76
- print(f" .grade(broken_config) = {score_broken:.4f} (broken input)")
77
- print(f" Genuine grading: fixed({score_gt:.2f}) >= broken({score_broken:.2f}) = {genuine}")
78
-
79
- print("\n" + "=" * 60)
80
- if all_pass:
81
- print("ALL TASKS PASSED β€” all scores strictly in (0, 1)")
82
- else:
83
- print("SOME TASKS FAILED β€” fix before pushing!")
84
- print("=" * 60)
85
-
86
-
87
- if __name__ == "__main__":
88
- test_grader_classes()