Dolphin-Syndrom commited on
Commit
d1cfa81
·
1 Parent(s): 92e5c18

v1 code review env

Browse files
README.md CHANGED
@@ -1,255 +1,152 @@
1
  ---
2
- title: Code Review Env Environment Server
3
- emoji: 🎬
4
- colorFrom: red
5
- colorTo: gray
6
  sdk: docker
7
  pinned: false
8
  app_port: 8000
9
  base_path: /web
10
  tags:
11
  - openenv
 
 
12
  ---
13
 
14
- # Code Review Env Environment
15
 
16
- A simple test environment that echoes back messages. Perfect for testing the env APIs as well as demonstrating environment usage patterns.
17
 
18
- ## Quick Start
19
 
20
- The simplest way to use the Code Review Env environment is through the `CodeReviewEnv` class:
 
 
21
 
22
- ```python
23
- from code_review_env import CodeReviewAction, CodeReviewEnv
24
 
25
- try:
26
- # Create environment from Docker image
27
- code_review_envenv = CodeReviewEnv.from_docker_image("code_review_env-env:latest")
28
 
29
- # Reset
30
- result = code_review_envenv.reset()
31
- print(f"Reset: {result.observation.echoed_message}")
32
 
33
- # Send multiple messages
34
- messages = ["Hello, World!", "Testing echo", "Final message"]
35
 
36
- for msg in messages:
37
- result = code_review_envenv.step(CodeReviewAction(message=msg))
38
- print(f"Sent: '{msg}'")
39
- print(f" → Echoed: '{result.observation.echoed_message}'")
40
- print(f" → Length: {result.observation.message_length}")
41
- print(f" → Reward: {result.reward}")
42
 
43
- finally:
44
- # Always clean up
45
- code_review_envenv.close()
46
- ```
47
-
48
- That's it! The `CodeReviewEnv.from_docker_image()` method handles:
49
- - Starting the Docker container
50
- - Waiting for the server to be ready
51
- - Connecting to the environment
52
- - Container cleanup when you call `close()`
53
-
54
- ## Building the Docker Image
55
-
56
- Before using the environment, you need to build the Docker image:
57
-
58
- ```bash
59
- # From project root
60
- docker build -t code_review_env-env:latest -f server/Dockerfile .
61
- ```
62
 
63
- ## Deploying to Hugging Face Spaces
64
 
65
- You can easily deploy your OpenEnv environment to Hugging Face Spaces using the `openenv push` command:
 
 
66
 
67
- ```bash
68
- # From the environment directory (where openenv.yaml is located)
69
- openenv push
70
-
71
- # Or specify options
72
- openenv push --namespace my-org --private
73
- ```
74
 
75
- The `openenv push` command will:
76
- 1. Validate that the directory is an OpenEnv environment (checks for `openenv.yaml`)
77
- 2. Prepare a custom build for Hugging Face Docker space (enables web interface)
78
- 3. Upload to Hugging Face (ensuring you're logged in)
79
 
80
- ### Prerequisites
81
 
82
- - Authenticate with Hugging Face: The command will prompt for login if not already authenticated
 
 
 
83
 
84
- ### Options
85
 
86
- - `--directory`, `-d`: Directory containing the OpenEnv environment (defaults to current directory)
87
- - `--repo-id`, `-r`: Repository ID in format 'username/repo-name' (defaults to 'username/env-name' from openenv.yaml)
88
- - `--base-image`, `-b`: Base Docker image to use (overrides Dockerfile FROM)
89
- - `--private`: Deploy the space as private (default: public)
90
 
91
- ### Examples
92
 
93
  ```bash
94
- # Push to your personal namespace (defaults to username/env-name from openenv.yaml)
95
- openenv push
96
-
97
- # Push to a specific repository
98
- openenv push --repo-id my-org/my-env
99
-
100
- # Push with a custom base image
101
- openenv push --base-image ghcr.io/meta-pytorch/openenv-base:latest
102
-
103
- # Push as a private space
104
- openenv push --private
105
-
106
- # Combine options
107
- openenv push --repo-id my-org/my-env --base-image custom-base:latest --private
108
  ```
109
 
110
- After deployment, your space will be available at:
111
- `https://huggingface.co/spaces/<repo-id>`
112
-
113
- The deployed space includes:
114
- - **Web Interface** at `/web` - Interactive UI for exploring the environment
115
- - **API Documentation** at `/docs` - Full OpenAPI/Swagger interface
116
- - **Health Check** at `/health` - Container health monitoring
117
- - **WebSocket** at `/ws` - Persistent session endpoint for low-latency interactions
118
-
119
- ## Environment Details
120
-
121
- ### Action
122
- **CodeReviewAction**: Contains a single field
123
- - `message` (str) - The message to echo back
124
-
125
- ### Observation
126
- **CodeReviewObservation**: Contains the echo response and metadata
127
- - `echoed_message` (str) - The message echoed back
128
- - `message_length` (int) - Length of the message
129
- - `reward` (float) - Reward based on message length (length × 0.1)
130
- - `done` (bool) - Always False for echo environment
131
- - `metadata` (dict) - Additional info like step count
132
-
133
- ### Reward
134
- The reward is calculated as: `message_length × 0.1`
135
- - "Hi" → reward: 0.2
136
- - "Hello, World!" → reward: 1.3
137
- - Empty message → reward: 0.0
138
-
139
- ## Advanced Usage
140
 
141
- ### Connecting to an Existing Server
142
-
143
- If you already have a Code Review Env environment server running, you can connect directly:
144
-
145
- ```python
146
- from code_review_env import CodeReviewEnv
147
-
148
- # Connect to existing server
149
- code_review_envenv = CodeReviewEnv(base_url="<ENV_HTTP_URL_HERE>")
150
-
151
- # Use as normal
152
- result = code_review_envenv.reset()
153
- result = code_review_envenv.step(CodeReviewAction(message="Hello!"))
154
  ```
155
 
156
- Note: When connecting to an existing server, `code_review_envenv.close()` will NOT stop the server.
157
 
158
- ### Using the Context Manager
159
 
160
- The client supports context manager usage for automatic connection management:
 
161
 
162
- ```python
163
- from code_review_env import CodeReviewAction, CodeReviewEnv
164
-
165
- # Connect with context manager (auto-connects and closes)
166
- with CodeReviewEnv(base_url="http://localhost:8000") as env:
167
- result = env.reset()
168
- print(f"Reset: {result.observation.echoed_message}")
169
- # Multiple steps with low latency
170
- for msg in ["Hello", "World", "!"]:
171
- result = env.step(CodeReviewAction(message=msg))
172
- print(f"Echoed: {result.observation.echoed_message}")
173
- ```
174
-
175
- The client uses WebSocket connections for:
176
- - **Lower latency**: No HTTP connection overhead per request
177
- - **Persistent session**: Server maintains your environment state
178
- - **Efficient for episodes**: Better for many sequential steps
179
-
180
- ### Concurrent WebSocket Sessions
181
 
182
- The server supports multiple concurrent WebSocket connections. To enable this,
183
- modify `server/app.py` to use factory mode:
184
 
185
- ```python
186
- # In server/app.py - use factory mode for concurrent sessions
187
- app = create_app(
188
- CodeReviewEnvironment, # Pass class, not instance
189
- CodeReviewAction,
190
- CodeReviewObservation,
191
- max_concurrent_envs=4, # Allow 4 concurrent sessions
192
- )
193
  ```
194
 
195
- Then multiple clients can connect simultaneously:
196
 
197
- ```python
198
- from code_review_env import CodeReviewAction, CodeReviewEnv
199
- from concurrent.futures import ThreadPoolExecutor
200
-
201
- def run_episode(client_id: int):
202
- with CodeReviewEnv(base_url="http://localhost:8000") as env:
203
- result = env.reset()
204
- for i in range(10):
205
- result = env.step(CodeReviewAction(message=f"Client {client_id}, step {i}"))
206
- return client_id, result.observation.message_length
207
-
208
- # Run 4 episodes concurrently
209
- with ThreadPoolExecutor(max_workers=4) as executor:
210
- results = list(executor.map(run_episode, range(4)))
211
  ```
212
 
213
- ## Development & Testing
214
-
215
- ### Direct Environment Testing
216
-
217
- Test the environment logic directly without starting the HTTP server:
218
 
219
  ```bash
220
- # From the server directory
221
- python3 server/code_review_env_environment.py
222
  ```
223
 
224
- This verifies that:
225
- - Environment resets correctly
226
- - Step executes actions properly
227
- - State tracking works
228
- - Rewards are calculated correctly
229
-
230
- ### Running Locally
231
-
232
- Run the server locally for development:
233
 
234
  ```bash
235
- uvicorn server.app:app --reload
236
  ```
237
 
238
- ## Project Structure
239
 
240
- ```
 
 
 
 
 
241
  code_review_env/
242
- ├── .dockerignore # Docker build exclusions
243
- ├── __init__.py # Module exports
244
- ├── README.md # This file
245
- ├── openenv.yaml # OpenEnv manifest
246
- ├── pyproject.toml # Project metadata and dependencies
247
- ├── uv.lock # Locked dependencies (generated)
248
- ├── client.py # CodeReviewEnv client
249
- ├── models.py # Action and Observation models
250
  └── server/
251
- ├── __init__.py # Server module exports
252
- ├── code_review_env_environment.py # Core environment logic
253
- ├── app.py # FastAPI application (HTTP + WebSocket endpoints)
254
- ── Dockerfile # Container image definition
 
 
 
255
  ```
 
1
  ---
2
+ title: Code Review Environment
3
+ emoji: 🧠
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: docker
7
  pinned: false
8
  app_port: 8000
9
  base_path: /web
10
  tags:
11
  - openenv
12
+ - reinforcement-learning
13
+ - code-review
14
  ---
15
 
16
+ # Code Review Environment (`code-review-env`)
17
 
18
+ This OpenEnv environment simulates a real software engineering task: reviewing buggy Python code and identifying security and logic issues with fixed taxonomy tags.
19
 
20
+ ## Why this environment
21
 
22
+ - Real-world utility: PR/code review is a common and valuable engineering workflow.
23
+ - RL-friendly: structured action space with dense rewards and deterministic grader.
24
+ - Progressive tasks: easy → medium → hard.
25
 
26
+ ## Action space
 
27
 
28
+ `ReviewAction` (`models.py`):
 
 
29
 
30
+ - `review_comment` (`str`): human-readable explanation
31
+ - `issues_found` (`list[str]`): tags from `ISSUE_TAXONOMY`
32
+ - `severity` (`low|medium|high|critical`)
33
 
34
+ ## Observation space
 
35
 
36
+ `ReviewObservation` (`models.py`):
 
 
 
 
 
37
 
38
+ - `task_id`, `file_name`, `task_description`
39
+ - `code_snippet` to review
40
+ - `feedback` after each step
41
+ - `step_number`, `reward`, `done`, `metadata`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
+ ## Tasks
44
 
45
+ - `task_easy`: `null_pointer`, `missing_return`
46
+ - `task_medium`: `sql_injection`, `hardcoded_secret`
47
+ - `task_hard`: `race_condition`, `improper_error_handling`, `timing_attack`
48
 
49
+ Defined in `server/tasks.py`.
 
 
 
 
 
 
50
 
51
+ ## Reward and grading
 
 
 
52
 
53
+ Implemented in `server/graders.py`:
54
 
55
+ - `base_score = |correctly_found| / |planted_issues|`
56
+ - `quality_bonus = +0.05` per correctly found issue with comment keywords
57
+ - `precision_penalty = -0.1` per false-positive issue
58
+ - final score clamped to `[0.0, 1.0]`
59
 
60
+ ## Required endpoints
61
 
62
+ - `GET /tasks`
63
+ - `POST /grader`
64
+ - `POST /baseline`
65
+ - plus OpenEnv core endpoints (`/reset`, `/step`, `/state`, `/health`, `/ws`)
66
 
67
+ ## Local setup
68
 
69
  ```bash
70
+ cd /home/manvith/OpenEnv/code_review_env
71
+ pip install -e .
72
+ pip install -r server/requirements.txt
73
+ uvicorn server.app:app --host 0.0.0.0 --port 8000
 
 
 
 
 
 
 
 
 
 
74
  ```
75
 
76
+ ## Manual API smoke test
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ ```bash
79
+ curl http://localhost:8000/health
80
+ curl http://localhost:8000/tasks
81
+ curl -X POST http://localhost:8000/baseline
82
+ curl -X POST http://localhost:8000/grader \
83
+ -H "Content-Type: application/json" \
84
+ -d '{"task_id":"task_easy","issues_found":["null_pointer"],"review_comment":"missing null check"}'
 
 
 
 
 
 
85
  ```
86
 
87
+ ## Baseline inference script
88
 
89
+ `inference.py` supports two modes:
90
 
91
+ 1. LLM mode (if `OPENAI_API_KEY` is set)
92
+ 2. Rule-based fallback (no key required)
93
 
94
+ ```bash
95
+ # Local fallback mode
96
+ python inference.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ # Local LLM mode
99
+ OPENAI_API_KEY=sk-... OPENAI_MODEL=gpt-4o-mini python inference.py
100
 
101
+ # Against HF Space
102
+ ENV_URL=https://<your-space>.hf.space python inference.py
 
 
 
 
 
 
103
  ```
104
 
105
+ Example output shape:
106
 
107
+ ```json
108
+ {
109
+ "task_easy": {"score": 0.85, "issues_found": ["null_pointer", "missing_return"]},
110
+ "task_medium": {"score": 0.7, "issues_found": ["sql_injection", "hardcoded_secret"]},
111
+ "task_hard": {"score": 0.5, "issues_found": ["race_condition", "improper_error_handling"]}
112
+ }
 
 
 
 
 
 
 
 
113
  ```
114
 
115
+ ## Docker
 
 
 
 
116
 
117
  ```bash
118
+ docker build -t code-review-env:latest -f server/Dockerfile .
119
+ docker run -p 8000:8000 code-review-env:latest
120
  ```
121
 
122
+ ## Deploy to Hugging Face Space
 
 
 
 
 
 
 
 
123
 
124
  ```bash
125
+ openenv push --repo-id YOUR_USERNAME/code-review-env
126
  ```
127
 
128
+ ## Submission links (both required)
129
 
130
+ 1. GitHub repository URL
131
+ 2. Hugging Face Space URL
132
+
133
+ ## Folder layout
134
+
135
+ ```text
136
  code_review_env/
137
+ ├── __init__.py
138
+ ├── client.py
139
+ ├── inference.py
140
+ ├── models.py
141
+ ├── openenv.yaml
142
+ ├── pyproject.toml
143
+ ├── README.md
 
144
  └── server/
145
+ ├── __init__.py
146
+ ├── app.py
147
+ ├── code_review_env_environment.py
148
+ ── graders.py
149
+ ├── tasks.py
150
+ ├── requirements.txt
151
+ └── Dockerfile
152
  ```
__init__.py CHANGED
@@ -7,10 +7,23 @@
7
  """Code Review Env Environment."""
8
 
9
  from .client import CodeReviewEnv
10
- from .models import CodeReviewAction, CodeReviewObservation
 
 
 
 
 
 
 
 
11
 
12
  __all__ = [
 
 
 
 
13
  "CodeReviewAction",
14
  "CodeReviewObservation",
 
15
  "CodeReviewEnv",
16
  ]
 
7
  """Code Review Env Environment."""
8
 
9
  from .client import CodeReviewEnv
10
+ from .models import (
11
+ ISSUE_TAXONOMY,
12
+ CodeReviewAction,
13
+ CodeReviewObservation,
14
+ CodeReviewState,
15
+ ReviewAction,
16
+ ReviewObservation,
17
+ ReviewState,
18
+ )
19
 
20
  __all__ = [
21
+ "ISSUE_TAXONOMY",
22
+ "ReviewAction",
23
+ "ReviewObservation",
24
+ "ReviewState",
25
  "CodeReviewAction",
26
  "CodeReviewObservation",
27
+ "CodeReviewState",
28
  "CodeReviewEnv",
29
  ]
__pycache__/__init__.cpython-312.pyc ADDED
Binary file (523 Bytes). View file
 
__pycache__/client.cpython-312.pyc ADDED
Binary file (2.88 kB). View file
 
__pycache__/inference.cpython-312.pyc CHANGED
Binary files a/__pycache__/inference.cpython-312.pyc and b/__pycache__/inference.cpython-312.pyc differ
 
__pycache__/models.cpython-312.pyc CHANGED
Binary files a/__pycache__/models.cpython-312.pyc and b/__pycache__/models.cpython-312.pyc differ
 
client.py CHANGED
@@ -4,96 +4,53 @@
4
  # This source code is licensed under the BSD-style license found in the
5
  # LICENSE file in the root directory of this source tree.
6
 
7
- """Code Review Env Environment Client."""
8
 
9
- from typing import Dict
10
 
11
  from openenv.core import EnvClient
12
  from openenv.core.client_types import StepResult
13
- from openenv.core.env_server.types import State
14
 
15
- from .models import CodeReviewAction, CodeReviewObservation
 
 
 
16
 
17
 
18
- class CodeReviewEnv(
19
- EnvClient[CodeReviewAction, CodeReviewObservation, State]
20
- ):
21
- """
22
- Client for the Code Review Env Environment.
23
 
24
- This client maintains a persistent WebSocket connection to the environment server,
25
- enabling efficient multi-step interactions with lower latency.
26
- Each client instance has its own dedicated environment session on the server.
27
-
28
- Example:
29
- >>> # Connect to a running server
30
- >>> with CodeReviewEnv(base_url="http://localhost:8000") as client:
31
- ... result = client.reset()
32
- ... print(result.observation.echoed_message)
33
- ...
34
- ... result = client.step(CodeReviewAction(message="Hello!"))
35
- ... print(result.observation.echoed_message)
36
-
37
- Example with Docker:
38
- >>> # Automatically start container and connect
39
- >>> client = CodeReviewEnv.from_docker_image("code_review_env-env:latest")
40
- >>> try:
41
- ... result = client.reset()
42
- ... result = client.step(CodeReviewAction(message="Test"))
43
- ... finally:
44
- ... client.close()
45
- """
46
-
47
- def _step_payload(self, action: CodeReviewAction) -> Dict:
48
- """
49
- Convert CodeReviewAction to JSON payload for step message.
50
-
51
- Args:
52
- action: CodeReviewAction instance
53
-
54
- Returns:
55
- Dictionary representation suitable for JSON encoding
56
- """
57
  return {
58
- "message": action.message,
 
 
59
  }
60
 
61
- def _parse_result(self, payload: Dict) -> StepResult[CodeReviewObservation]:
62
- """
63
- Parse server response into StepResult[CodeReviewObservation].
64
-
65
- Args:
66
- payload: JSON response data from server
67
-
68
- Returns:
69
- StepResult with CodeReviewObservation
70
- """
71
  obs_data = payload.get("observation", {})
72
- observation = CodeReviewObservation(
73
- echoed_message=obs_data.get("echoed_message", ""),
74
- message_length=obs_data.get("message_length", 0),
 
 
 
 
 
75
  done=payload.get("done", False),
76
  reward=payload.get("reward"),
77
  metadata=obs_data.get("metadata", {}),
78
  )
79
-
80
  return StepResult(
81
  observation=observation,
82
  reward=payload.get("reward"),
83
  done=payload.get("done", False),
84
  )
85
 
86
- def _parse_state(self, payload: Dict) -> State:
87
- """
88
- Parse server response into State object.
89
-
90
- Args:
91
- payload: JSON response from state request
92
-
93
- Returns:
94
- State object with episode_id and step_count
95
- """
96
- return State(
97
- episode_id=payload.get("episode_id"),
98
  step_count=payload.get("step_count", 0),
 
 
99
  )
 
4
  # This source code is licensed under the BSD-style license found in the
5
  # LICENSE file in the root directory of this source tree.
6
 
7
+ """Client for the Code Review OpenEnv environment."""
8
 
9
+ from typing import Any
10
 
11
  from openenv.core import EnvClient
12
  from openenv.core.client_types import StepResult
 
13
 
14
+ try:
15
+ from .models import ReviewAction, ReviewObservation, ReviewState
16
+ except ImportError:
17
+ from models import ReviewAction, ReviewObservation, ReviewState
18
 
19
 
20
+ class CodeReviewEnv(EnvClient[ReviewAction, ReviewObservation, ReviewState]):
21
+ """WebSocket client for interacting with the code review environment."""
 
 
 
22
 
23
+ def _step_payload(self, action: ReviewAction) -> dict[str, Any]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  return {
25
+ "review_comment": action.review_comment,
26
+ "issues_found": action.issues_found,
27
+ "severity": action.severity,
28
  }
29
 
30
+ def _parse_result(self, payload: dict[str, Any]) -> StepResult[ReviewObservation]:
 
 
 
 
 
 
 
 
 
31
  obs_data = payload.get("observation", {})
32
+ observation = ReviewObservation(
33
+ task_id=obs_data.get("task_id", "task_easy"),
34
+ file_name=obs_data.get("file_name", ""),
35
+ task_description=obs_data.get("task_description", ""),
36
+ code_snippet=obs_data.get("code_snippet", ""),
37
+ feedback=obs_data.get("feedback", ""),
38
+ step_number=obs_data.get("step_number", 0),
39
+ available_issue_tags=obs_data.get("available_issue_tags", []),
40
  done=payload.get("done", False),
41
  reward=payload.get("reward"),
42
  metadata=obs_data.get("metadata", {}),
43
  )
 
44
  return StepResult(
45
  observation=observation,
46
  reward=payload.get("reward"),
47
  done=payload.get("done", False),
48
  )
49
 
50
+ def _parse_state(self, payload: dict[str, Any]) -> ReviewState:
51
+ return ReviewState(
52
+ episode_id=payload.get("episode_id", ""),
 
 
 
 
 
 
 
 
 
53
  step_count=payload.get("step_count", 0),
54
+ current_task_id=payload.get("current_task_id", "task_easy"),
55
+ max_steps=payload.get("max_steps", 3),
56
  )
inference.py CHANGED
@@ -2,18 +2,25 @@ import json
2
  import os
3
  import re
4
  import sys
 
5
  from typing import Any
6
 
7
- import httpx
8
  from openai import OpenAI
9
 
 
 
 
 
 
 
 
10
 
11
  TASK_IDS = ["task_easy", "task_medium", "task_hard"]
12
  DEFAULT_ENV_URL = "http://localhost:8000"
13
  DEFAULT_MODEL = "gpt-4o-mini"
14
 
15
 
16
- DETECTION_RULES: dict[str, callable] = {
17
  "null_pointer": lambda code: ".get(" in code or "= None" in code,
18
  "missing_return": lambda code: "# todo: return" in code.lower(),
19
  "sql_injection": lambda code: (
@@ -134,16 +141,14 @@ def run_baseline() -> dict[str, dict[str, Any]]:
134
 
135
  results: dict[str, dict[str, Any]] = {}
136
 
137
- with httpx.Client(timeout=30.0) as client:
138
  for task_id in TASK_IDS:
139
- reset_response = client.post(f"{env_url}/reset", json={"task_id": task_id})
140
- reset_response.raise_for_status()
141
- reset_payload = reset_response.json()
142
 
143
- observation = reset_payload.get("observation", {})
144
- code_snippet = str(observation.get("code_snippet", ""))
145
- file_name = str(observation.get("file_name", ""))
146
- task_description = str(observation.get("task_description", ""))
147
 
148
  action_payload: dict[str, Any]
149
  if openai_client:
@@ -161,11 +166,8 @@ def run_baseline() -> dict[str, dict[str, Any]]:
161
  else:
162
  action_payload = build_rule_action(code_snippet)
163
 
164
- step_response = client.post(f"{env_url}/step", json={"action": action_payload})
165
- step_response.raise_for_status()
166
- step_payload = step_response.json()
167
-
168
- score = float(step_payload.get("reward") or 0.0)
169
  results[task_id] = {
170
  "score": score,
171
  "issues_found": action_payload.get("issues_found", []),
 
2
  import os
3
  import re
4
  import sys
5
+ from collections.abc import Callable
6
  from typing import Any
7
 
 
8
  from openai import OpenAI
9
 
10
+ try:
11
+ from .client import CodeReviewEnv
12
+ from .models import ReviewAction
13
+ except ImportError:
14
+ from client import CodeReviewEnv
15
+ from models import ReviewAction
16
+
17
 
18
  TASK_IDS = ["task_easy", "task_medium", "task_hard"]
19
  DEFAULT_ENV_URL = "http://localhost:8000"
20
  DEFAULT_MODEL = "gpt-4o-mini"
21
 
22
 
23
+ DETECTION_RULES: dict[str, Callable[[str], bool]] = {
24
  "null_pointer": lambda code: ".get(" in code or "= None" in code,
25
  "missing_return": lambda code: "# todo: return" in code.lower(),
26
  "sql_injection": lambda code: (
 
141
 
142
  results: dict[str, dict[str, Any]] = {}
143
 
144
+ with CodeReviewEnv(base_url=env_url).sync() as env:
145
  for task_id in TASK_IDS:
146
+ reset_result = env.reset(task_id=task_id)
147
+ observation = reset_result.observation
 
148
 
149
+ code_snippet = observation.code_snippet
150
+ file_name = observation.file_name
151
+ task_description = observation.task_description
 
152
 
153
  action_payload: dict[str, Any]
154
  if openai_client:
 
166
  else:
167
  action_payload = build_rule_action(code_snippet)
168
 
169
+ step_result = env.step(ReviewAction.model_validate(action_payload))
170
+ score = float(step_result.reward or 0.0)
 
 
 
171
  results[task_id] = {
172
  "score": score,
173
  "issues_found": action_payload.get("issues_found", []),
openenv.yaml CHANGED
@@ -1,5 +1,17 @@
1
  spec_version: 1
2
- name: code_review_env
 
 
 
 
 
 
 
 
 
 
 
 
3
  type: space
4
  runtime: fastapi
5
  app: server.app:app
 
1
  spec_version: 1
2
+ name: code-review-env
3
+ version: "1.0.0"
4
+ description: >
5
+ A code review agent environment where an AI agent reads buggy Python code
6
+ and learns to identify security vulnerabilities, logic errors, and code smells.
7
+ Simulates the real-world software engineering task of pull request review.
8
+ author: YOUR_HF_USERNAME
9
+ tasks:
10
+ - task_easy
11
+ - task_medium
12
+ - task_hard
13
+ sdk: gradio
14
+ sdk_version: "4.0"
15
  type: space
16
  runtime: fastapi
17
  app: server.app:app
server/Dockerfile CHANGED
@@ -4,77 +4,19 @@
4
  # This source code is licensed under the BSD-style license found in the
5
  # LICENSE file in the root directory of this source tree.
6
 
7
- # Multi-stage build using openenv-base
8
- # This Dockerfile is flexible and works for both:
9
- # - In-repo environments (with local OpenEnv sources)
10
- # - Standalone environments (with openenv from PyPI/Git)
11
- # The build script (openenv build) handles context detection and sets appropriate build args.
12
-
13
  ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest
14
- FROM ${BASE_IMAGE} AS builder
15
-
16
- WORKDIR /app
17
-
18
- # Ensure git is available (required for installing dependencies from VCS)
19
- RUN apt-get update && \
20
- apt-get install -y --no-install-recommends git && \
21
- rm -rf /var/lib/apt/lists/*
22
-
23
- # Build argument to control whether we're building standalone or in-repo
24
- ARG BUILD_MODE=in-repo
25
- ARG ENV_NAME=code_review_env
26
-
27
- # Copy environment code (always at root of build context)
28
- COPY . /app/env
29
-
30
- # For in-repo builds, openenv is already vendored in the build context
31
- # For standalone builds, openenv will be installed via pyproject.toml
32
- WORKDIR /app/env
33
-
34
- # Ensure uv is available (for local builds where base image lacks it)
35
- RUN if ! command -v uv >/dev/null 2>&1; then \
36
- curl -LsSf https://astral.sh/uv/install.sh | sh && \
37
- mv /root/.local/bin/uv /usr/local/bin/uv && \
38
- mv /root/.local/bin/uvx /usr/local/bin/uvx; \
39
- fi
40
-
41
- # Install dependencies using uv sync
42
- # If uv.lock exists, use it; otherwise resolve on the fly
43
- RUN --mount=type=cache,target=/root/.cache/uv \
44
- if [ -f uv.lock ]; then \
45
- uv sync --frozen --no-install-project --no-editable; \
46
- else \
47
- uv sync --no-install-project --no-editable; \
48
- fi
49
-
50
- RUN --mount=type=cache,target=/root/.cache/uv \
51
- if [ -f uv.lock ]; then \
52
- uv sync --frozen --no-editable; \
53
- else \
54
- uv sync --no-editable; \
55
- fi
56
-
57
- # Final runtime stage
58
  FROM ${BASE_IMAGE}
59
 
60
  WORKDIR /app
61
 
62
- # Copy the virtual environment from builder
63
- COPY --from=builder /app/env/.venv /app/.venv
64
-
65
- # Copy the environment code
66
- COPY --from=builder /app/env /app/env
67
-
68
- # Set PATH to use the virtual environment
69
- ENV PATH="/app/.venv/bin:$PATH"
70
 
71
- # Set PYTHONPATH so imports work correctly
72
- ENV PYTHONPATH="/app/env:$PYTHONPATH"
73
 
74
  # Health check
75
  HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
76
  CMD curl -f http://localhost:8000/health || exit 1
77
 
78
- # Run the FastAPI server
79
- # The module path is constructed to work with the /app/env structure
80
- CMD ["sh", "-c", "cd /app/env && uvicorn server.app:app --host 0.0.0.0 --port 8000"]
 
4
  # This source code is licensed under the BSD-style license found in the
5
  # LICENSE file in the root directory of this source tree.
6
 
 
 
 
 
 
 
7
  ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  FROM ${BASE_IMAGE}
9
 
10
  WORKDIR /app
11
 
12
+ COPY server/requirements.txt /tmp/requirements.txt
13
+ RUN pip install --no-cache-dir -r /tmp/requirements.txt
 
 
 
 
 
 
14
 
15
+ COPY . /app/
 
16
 
17
  # Health check
18
  HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
19
  CMD curl -f http://localhost:8000/health || exit 1
20
 
21
+ EXPOSE 8000
22
+ CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "8000"]
 
server/__init__.py CHANGED
@@ -7,5 +7,7 @@
7
  """Code Review Env environment server components."""
8
 
9
  from .code_review_env_environment import CodeReviewEnvironment
 
 
10
 
11
- __all__ = ["CodeReviewEnvironment"]
 
7
  """Code Review Env environment server components."""
8
 
9
  from .code_review_env_environment import CodeReviewEnvironment
10
+ from .graders import grade_review
11
+ from .tasks import TASKS, Task, get_task
12
 
13
+ __all__ = ["CodeReviewEnvironment", "Task", "TASKS", "get_task", "grade_review"]
server/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/server/__pycache__/__init__.cpython-312.pyc and b/server/__pycache__/__init__.cpython-312.pyc differ
 
server/__pycache__/app.cpython-312.pyc CHANGED
Binary files a/server/__pycache__/app.cpython-312.pyc and b/server/__pycache__/app.cpython-312.pyc differ
 
server/__pycache__/tasks.cpython-312.pyc CHANGED
Binary files a/server/__pycache__/tasks.cpython-312.pyc and b/server/__pycache__/tasks.cpython-312.pyc differ
 
server/app.py CHANGED
@@ -22,7 +22,7 @@ try:
22
  from .code_review_env_environment import CodeReviewEnvironment
23
  from .graders import grade_review
24
  from .tasks import TASKS, Task, get_task
25
- except ModuleNotFoundError:
26
  from models import ReviewAction, ReviewObservation
27
  from server.code_review_env_environment import CodeReviewEnvironment
28
  from server.graders import grade_review
 
22
  from .code_review_env_environment import CodeReviewEnvironment
23
  from .graders import grade_review
24
  from .tasks import TASKS, Task, get_task
25
+ except ImportError:
26
  from models import ReviewAction, ReviewObservation
27
  from server.code_review_env_environment import CodeReviewEnvironment
28
  from server.graders import grade_review
server/requirements.txt CHANGED
@@ -1,6 +1,7 @@
1
- openenv[core]>=0.2.0
2
  fastapi>=0.115.0
3
  uvicorn>=0.24.0
 
4
  openai>=1.0
5
  httpx>=0.24.0
6
 
 
1
+ openenv-core>=0.2.0
2
  fastapi>=0.115.0
3
  uvicorn>=0.24.0
4
+ pydantic>=2.0
5
  openai>=1.0
6
  httpx>=0.24.0
7
 
server/tasks.py CHANGED
@@ -1,6 +1,9 @@
1
  from dataclasses import dataclass
2
 
3
- from ..models import ISSUE_TAXONOMY
 
 
 
4
 
5
 
6
  @dataclass(frozen=True)
 
1
  from dataclasses import dataclass
2
 
3
+ try:
4
+ from ..models import ISSUE_TAXONOMY
5
+ except ImportError:
6
+ from models import ISSUE_TAXONOMY
7
 
8
 
9
  @dataclass(frozen=True)