Yero commited on
Commit
c193fbb
·
1 Parent(s): 1680cfc

cleanup comments and readme

Browse files
Dockerfile CHANGED
@@ -5,5 +5,6 @@ RUN pip install --no-cache-dir -r requirements.txt
5
  COPY . .
6
  ENV PYTHONPATH=/
7
  ENV PYTHONIOENCODING=utf-8
 
8
  EXPOSE 7860
9
  CMD ["python", "-m", "uvicorn", "code_review_env.server.app:app", "--host", "0.0.0.0", "--port", "7860"]
 
5
  COPY . .
6
  ENV PYTHONPATH=/
7
  ENV PYTHONIOENCODING=utf-8
8
+ ENV ENABLE_WEB_INTERFACE=true
9
  EXPOSE 7860
10
  CMD ["python", "-m", "uvicorn", "code_review_env.server.app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -9,125 +9,118 @@ tags:
9
  - openenv
10
  ---
11
 
12
- # CodeReviewEnv: Triage CVEs Like a Pro
13
 
14
- *Built for the Meta/PyTorch OpenEnv Hackathon*
15
 
16
- Hey! 👋 Welcome to **CodeReviewEnv**.
17
 
18
- Most RL environments are toy setups like GridWorld or simple mazes. We wanted to tackle a real problem that we actually face as developers: **Vulnerability Triage**.
19
 
20
- We built `CodeReviewEnv` using real-world data scraped directly from patches in the National Vulnerability Database (NVD). The agent connects to a repository, scans the files, and uses heuristics (like codebase churn, complexity, and recency) to decide whether to `flag` a file for manual security review or `skip` it and move on.
21
-
22
- * 🚀 **Hugging Face Space (Live Environment)**: [https://huggingface.co/spaces/lucid987654/code-review-env](https://huggingface.co/spaces/lucid987654/code-review-env)
23
- * 📁 **GitHub Repository**: [https://github.com/subwaycookiecrunch/Meta-project](https://github.com/subwaycookiecrunch/Meta-project)
24
 
25
  ---
26
 
27
- ### The Problem
28
 
29
- We have 1715 files across 65 authentic CVEs pulled directly from actual GitHub vulnerabilities.
30
- We hit the agent with **Asymmetric Rewards**. In the real world, missing a critical bug (False Negative) is infinitely worse than accidentally flagging a safe file for review (False Positive).
31
 
32
- Our reward table forces the agent to balance its paranoia:
33
 
34
- | Outcome | Reward | Rationale |
35
- |---------|--------|-----------|
36
- | True Positive (found a real bug) | **+1.0** | Highest reward — catching vulnerabilities is the goal |
37
- | True Negative (correctly skipped safe file) | **+0.8** | Good judgment that saves review budget |
38
- | False Positive (flagged a safe file) | **-0.4** | Wastes review budget, penalized |
39
- | False Negative (missed a real bug) | **-0.2** | Worst failure — penalty provides learning signal |
40
- | Over-budget flag attempt | **-0.5** | Hard constraint — budget is non-negotiable |
41
 
42
- Oh, and there's a strict **Review Budget**. You can't just flag everything, or you run out of budget and get heavily penalized!
 
 
 
 
 
 
43
 
44
- ---
 
 
 
 
 
 
45
 
46
- ### 🧩 Environment Specifications
47
 
48
- #### Action Space
49
- The action space is a straightforward, string-based categorical action:
50
- * `decision`: `"flag"` or `"skip"`
51
 
52
- #### Observation Space
53
- At each step, the environment provides a rich state vector. The key metrics include:
54
- * `file_path` & tracking metrics (`file_index`, `files_remaining`, `total_files`)
55
- * **Difficulty & Context**: `difficulty_level`, `cve_id`, `repo_name`
56
- * **Static Analysis Features**:
57
- * `churn_score` — lines changed in the file (higher = more volatile)
58
- * `complexity_score` — cyclomatic complexity proxy (higher = harder to review)
59
- * `todo_score` — count of TODOs/FIXMEs (higher = more tech debt)
60
- * `recency_score` — how recently the file was modified (higher = more recent)
61
- * **Limits**: `review_budget` and `files_flagged`
62
- * **Terminal Metrics**: `precision`, `recall`, `f1_score`, `true_positives`, `false_positives`, `false_negatives`, `true_negatives`
63
 
64
- #### 🎯 The Three Tasks (Difficulty Tiers)
65
- We've partitioned the environment into three distinct difficulty tasks, scaling gracefully by the size of the repository logic the agent needs to parse over its fixed review budget:
66
- 1. **Easy (`difficulty="easy"`)**: Small repositories and pull requests (≤ 15 files). The budget is relatively forgiving.
67
- 2. **Medium (`difficulty="medium"`)**: Average-sized PRs (16-29 files) requiring more scrutiny.
68
- 3. **Hard (`difficulty="hard"`)**: Large-scale patches (30+ files). The agent is strapped for budget and must be extremely selective about utilizing its flags.
 
69
 
70
- Each task includes a **programmatic grader** that returns a score between 0.0 and 1.0 (the F1-score), combining precision and recall into a single metric. The grader is deterministic and reproducible.
71
 
72
  ---
73
 
74
- ## Setup & Running
75
 
76
- **1. Install deps:**
77
  ```bash
78
  pip install openenv-core openai
79
  ```
80
 
81
- **2. Spin up the FastAPI Server via Docker:**
82
  ```bash
83
  docker build -t codereviewenv .
84
  docker run -p 7860:7860 codereviewenv
85
  ```
86
- *(If you are viewing this on Hugging Face Spaces, the server is automatically running!)*
87
 
88
- **3. Run the inference script:**
89
  ```bash
90
- export HF_TOKEN="your_huggingface_token"
91
  python inference.py
92
  ```
93
 
94
  ---
95
 
96
- ## The Agents (We built two!)
 
 
97
 
98
- ### 1. The Zero-Shot LLM Baseline (`inference.py`)
99
- This is the standard OpenEnv submission script required by the Hackathon. We wrote a wrapper that passes the environment state into an OpenAI-compatible LLM to see if a huge model can reason through the file stats to allocate its budget. It evaluates the environment sequentially over the **Easy**, **Medium**, and **Hard** tasks.
100
 
101
- **Baseline Scores:**
102
 
103
- | Difficulty | Model | F1-Score | Precision | Recall |
104
- |-----------|-------|----------|-----------|--------|
105
- | Easy | Qwen2.5-Coder-32B | ~0.15 | ~0.12 | ~0.25 |
106
- | Medium | Qwen2.5-Coder-32B | ~0.10 | ~0.08 | ~0.18 |
107
- | Hard | Qwen2.5-Coder-32B | ~0.08 | ~0.06 | ~0.15 |
108
 
109
- These are zero-shot scores — the LLM has no training on this specific task, demonstrating there is real room for improvement via RL training.
110
 
111
  ```bash
112
- export HF_TOKEN="your_huggingface_token"
113
  python inference.py
114
  ```
115
 
116
- ### 2. The Native PyTorch Agent (`train_pytorch_agent.py`)
117
- **Flex Warning.** LLMs are cool, but they struggle to implicitly understand strict mathematical bounds (like rationing a flag budget perfectly over exactly 29 files with precise asymmetric scoring). So, we went bare-metal.
 
118
 
119
- We built a custom Deep Reinforcement Learning Agent using native PyTorch Policy Gradients (REINFORCE) to interface perfectly with the OpenEnv API. It iteratively converges to find the perfect risk/reward strategy.
120
  ```bash
121
  pip install torch
122
  python train_pytorch_agent.py
123
  ```
124
 
125
- ## Hackathon Repo Tour
126
- * `Dockerfile` & `openenv.yaml`: The OpenEnv backend deployment wrappers
127
- * `inference.py`: The mandatory LLM endpoint validation script
128
- * `train_pytorch_agent.py`: Our custom PyTorch REINFORCE brain
129
- * `/models.py`: Pydantic Models for Actions & Observations
130
- * `/server/environment.py`: Where the magic reward mathematics happen
131
- * `/data/`: The actual scraped CVE GitHub dataset
 
132
 
133
- *MIT License. Thanks for checking it out!*
 
9
  - openenv
10
  ---
11
 
12
+ # CodeReviewEnv
13
 
14
+ An RL environment for vulnerability triage, built on real CVE data from the NVD.
15
 
16
+ The idea: most RL envs are toy problems (gridworld, cartpole, etc). We wanted something closer to what devs actually deal with — triaging security patches across a codebase with limited time and attention.
17
 
18
+ The agent gets a stream of files from a real CVE patch and has to decide: **flag** this file for human review, or **skip** it. There's a fixed review budget so you can't just flag everything.
19
 
20
+ * **HF Space**: https://huggingface.co/spaces/lucid987654/code-review-env
21
+ * **GitHub**: https://github.com/subwaycookiecrunch/Meta-project
 
 
22
 
23
  ---
24
 
25
+ ### Data
26
 
27
+ 1715 files across 65 CVEs scraped from actual GitHub vulnerability patches. Each file has four features extracted from the commit history: churn, complexity, TODO count, and recency.
 
28
 
29
+ ### Rewards
30
 
31
+ Asymmetric on purpose missing a real bug is worse than wasting a review slot on a clean file.
 
 
 
 
 
 
32
 
33
+ | Outcome | Reward | Why |
34
+ |---------|--------|-----|
35
+ | True Positive | +1.0 | found a real bug |
36
+ | True Negative | +0.8 | correctly skipped clean file |
37
+ | False Positive | -0.4 | wasted budget on safe file |
38
+ | False Negative | -0.2 | missed a bug |
39
+ | Over-budget flag | -0.5 | budget is a hard limit |
40
 
41
+ ### Tasks
42
+
43
+ Three difficulty levels based on repo size:
44
+
45
+ - **easy**: ≤15 files, generous budget
46
+ - **medium**: 16-29 files
47
+ - **hard**: 30+ files, tight budget — agent really has to pick its spots
48
 
49
+ Grading is F1 score (precision × recall), always in [0, 1].
50
 
51
+ ---
 
 
52
 
53
+ ### Observation fields
 
 
 
 
 
 
 
 
 
 
54
 
55
+ Each step gives you:
56
+ - `file_path`, `file_index`, `total_files`, `files_remaining`
57
+ - `churn_score`, `complexity_score`, `todo_score`, `recency_score`
58
+ - `review_budget`, `files_flagged`
59
+ - `difficulty_level`, `cve_id`, `repo_name`
60
+ - terminal: `precision`, `recall`, `f1_score`, `true_positives`, `false_positives`, etc.
61
 
62
+ Action is just `{"decision": "flag"}` or `{"decision": "skip"}`.
63
 
64
  ---
65
 
66
+ ## Running it
67
 
68
+ **Install:**
69
  ```bash
70
  pip install openenv-core openai
71
  ```
72
 
73
+ **Docker:**
74
  ```bash
75
  docker build -t codereviewenv .
76
  docker run -p 7860:7860 codereviewenv
77
  ```
 
78
 
79
+ **Inference:**
80
  ```bash
81
+ export HF_TOKEN="your_token"
82
  python inference.py
83
  ```
84
 
85
  ---
86
 
87
+ ## Agents
88
+
89
+ ### LLM baseline (`inference.py`)
90
 
91
+ Sends the file stats to Qwen2.5-Coder-32B via the HF inference API and asks it to flag or skip. Runs all three difficulty levels.
 
92
 
93
+ Rough zero-shot numbers:
94
 
95
+ | Difficulty | F1 | Precision | Recall |
96
+ |---|---|---|---|
97
+ | Easy | ~0.15 | ~0.12 | ~0.25 |
98
+ | Medium | ~0.10 | ~0.08 | ~0.18 |
99
+ | Hard | ~0.08 | ~0.06 | ~0.15 |
100
 
101
+ Lots of room to improve — the LLM has no training signal, it's just guessing from feature names.
102
 
103
  ```bash
104
+ export HF_TOKEN="your_token"
105
  python inference.py
106
  ```
107
 
108
+ ### PyTorch agent (`train_pytorch_agent.py`)
109
+
110
+ REINFORCE with a 3-layer MLP. Takes the 6 observation features as input, outputs flag/skip probabilities. Trains directly against the env reward signal.
111
 
 
112
  ```bash
113
  pip install torch
114
  python train_pytorch_agent.py
115
  ```
116
 
117
+ ## File layout
118
+
119
+ - `Dockerfile` + `openenv.yaml` deployment config
120
+ - `inference.py` LLM baseline (hackathon submission script)
121
+ - `train_pytorch_agent.py` pytorch RL agent
122
+ - `models.py` pydantic action/observation/state types
123
+ - `server/environment.py` core env logic + reward math
124
+ - `data/` — the CVE dataset
125
 
126
+ MIT License
__pycache__/client.cpython-312.pyc CHANGED
Binary files a/__pycache__/client.cpython-312.pyc and b/__pycache__/client.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
@@ -51,7 +51,9 @@ class CodeReviewEnv(EnvClient[CodeReviewAction, CodeReviewObservation, CodeRevie
51
  total_files=payload.get("total_files", 0),
52
  total_bugs=payload.get("total_bugs", 0),
53
  current_file_index=payload.get("current_file_index", 0),
 
54
  files_flagged=payload.get("files_flagged", 0),
55
  correct_flags=payload.get("correct_flags", 0),
56
  review_budget=payload.get("review_budget", 0),
 
57
  )
 
51
  total_files=payload.get("total_files", 0),
52
  total_bugs=payload.get("total_bugs", 0),
53
  current_file_index=payload.get("current_file_index", 0),
54
+ difficulty_level=payload.get("difficulty_level", "medium"),
55
  files_flagged=payload.get("files_flagged", 0),
56
  correct_flags=payload.get("correct_flags", 0),
57
  review_budget=payload.get("review_budget", 0),
58
+ cumulative_reward=payload.get("cumulative_reward", 0.0),
59
  )
code_review_env.egg-info/PKG-INFO CHANGED
@@ -10,81 +10,141 @@ Requires-Dist: openenv-core>=0.2.0
10
  Requires-Dist: fastapi>=0.100.0
11
  Requires-Dist: uvicorn>=0.20.0
12
  Requires-Dist: pydantic>=2.0.0
 
 
 
 
13
 
14
- # CodeReviewEnv
 
 
 
 
 
 
 
 
 
15
 
16
- RL environment for training agents to triage code files for vulnerabilities. Built on top of OpenEnv (Meta/PyTorch).
17
 
18
- Uses real labeled data from NVD - 1715 file samples across 65 CVEs. The agent reviews files from a repo and has to figure out which ones are actually buggy based on features like churn, complexity, TODO count, and recency.
19
 
20
- ## How it works
21
 
22
- Each episode loads files from a real CVE-affected codebase. The agent goes through files one by one and decides `flag` or `skip`. At the end it gets scored on precision/recall/f1.
23
 
24
- Rewards are intentionally asymmetric - missing a real bug hurts way more than flagging a false positive, because thats how it works in practice.
25
 
26
- | outcome | reward |
27
- |---------|--------|
28
- | correctly flag a bug | 1.0 |
29
- | correctly skip safe file | 0.8 |
30
- | flag a safe file | 0.4 |
31
- | miss a real bug | 0.0 |
32
 
33
- The agent also has a limited flag budget per episode so it can't just flag everything.
34
 
35
- ## Setup
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  ```bash
38
  pip install openenv-core openai
39
  ```
40
 
41
- To run the server locally:
42
  ```bash
43
  docker build -t codereviewenv .
44
- docker run -p 8000:8000 codereviewenv
45
  ```
 
46
 
47
- ## Client usage
48
-
49
- To run the evaluation script:
50
  ```bash
51
- export HF_TOKEN="your_hugging_face_token"
52
  python inference.py
53
  ```
54
 
55
- ## Observation fields
56
 
57
- - `file_path` - current file being reviewed
58
- - `churn_score` / `complexity_score` / `todo_score` / `recency_score` - features (0-100)
59
- - `review_budget` - how many flags left
60
- - `files_remaining` - files left in episode
61
- - `cve_id` / `cvss_score` - which vulnerability this episode is about
62
 
63
- ## Data
 
64
 
65
- The training data comes from DeathClock's CVE pipeline:
66
- 1. Pull CVEs from NVD API with GitHub refs
67
- 2. Fetch file metadata and commit history from those repos
68
- 3. Compute feature scores for each file
69
- 4. Label files based on which ones were actually patched in the CVE fix
70
 
71
- 65 CVEs, 1715 files total. 12 confirmed buggy files across 7 episodes - rest are negative examples that the agent needs to learn to skip.
 
 
 
 
72
 
73
- ## Files
74
 
 
 
 
75
  ```
76
- code_review_env/
77
- ├── Dockerfile - project root dockerfile (for openenv)
78
- ├── inference.py - generic evaluator script
79
- ├── models.py - action/observation/state types
80
- ├── client.py - websocket client
81
- ├── demo.py - runs baseline agents locally without docker
82
- ├── data/ - cve training data (json)
83
- └── server/
84
- ├── environment.py - core env logic
85
- └── app.py - fastapi server
86
  ```
87
 
88
- ## License
 
 
 
 
 
 
89
 
90
- MIT
 
10
  Requires-Dist: fastapi>=0.100.0
11
  Requires-Dist: uvicorn>=0.20.0
12
  Requires-Dist: pydantic>=2.0.0
13
+ Provides-Extra: train
14
+ Requires-Dist: torch>=2.0.0; extra == "train"
15
+ Provides-Extra: inference
16
+ Requires-Dist: openai>=1.0.0; extra == "inference"
17
 
18
+ ---
19
+ title: CodeReviewEnv
20
+ emoji: 🛡️
21
+ colorFrom: yellow
22
+ colorTo: red
23
+ sdk: docker
24
+ pinned: false
25
+ tags:
26
+ - openenv
27
+ ---
28
 
29
+ # CodeReviewEnv: Triage CVEs Like a Pro
30
 
31
+ *Built for the Meta/PyTorch OpenEnv Hackathon*
32
 
33
+ Hey! 👋 Welcome to **CodeReviewEnv**.
34
 
35
+ Most RL environments are toy setups like GridWorld or simple mazes. We wanted to tackle a real problem that we actually face as developers: **Vulnerability Triage**.
36
 
37
+ We built `CodeReviewEnv` using real-world data scraped directly from patches in the National Vulnerability Database (NVD). The agent connects to a repository, scans the files, and uses heuristics (like codebase churn, complexity, and recency) to decide whether to `flag` a file for manual security review or `skip` it and move on.
38
 
39
+ * 🚀 **Hugging Face Space (Live Environment)**: [https://huggingface.co/spaces/lucid987654/code-review-env](https://huggingface.co/spaces/lucid987654/code-review-env)
40
+ * 📁 **GitHub Repository**: [https://github.com/subwaycookiecrunch/Meta-project](https://github.com/subwaycookiecrunch/Meta-project)
 
 
 
 
41
 
42
+ ---
43
 
44
+ ### The Problem
45
 
46
+ We have 1715 files across 65 authentic CVEs pulled directly from actual GitHub vulnerabilities.
47
+ We hit the agent with **Asymmetric Rewards**. In the real world, missing a critical bug (False Negative) is infinitely worse than accidentally flagging a safe file for review (False Positive).
48
+
49
+ Our reward table forces the agent to balance its paranoia:
50
+
51
+ | Outcome | Reward | Rationale |
52
+ |---------|--------|-----------|
53
+ | True Positive (found a real bug) | **+1.0** | Highest reward — catching vulnerabilities is the goal |
54
+ | True Negative (correctly skipped safe file) | **+0.8** | Good judgment that saves review budget |
55
+ | False Positive (flagged a safe file) | **-0.4** | Wastes review budget, penalized |
56
+ | False Negative (missed a real bug) | **-0.2** | Worst failure — penalty provides learning signal |
57
+ | Over-budget flag attempt | **-0.5** | Hard constraint — budget is non-negotiable |
58
+
59
+ Oh, and there's a strict **Review Budget**. You can't just flag everything, or you run out of budget and get heavily penalized!
60
+
61
+ ---
62
+
63
+ ### 🧩 Environment Specifications
64
+
65
+ #### Action Space
66
+ The action space is a straightforward, string-based categorical action:
67
+ * `decision`: `"flag"` or `"skip"`
68
+
69
+ #### Observation Space
70
+ At each step, the environment provides a rich state vector. The key metrics include:
71
+ * `file_path` & tracking metrics (`file_index`, `files_remaining`, `total_files`)
72
+ * **Difficulty & Context**: `difficulty_level`, `cve_id`, `repo_name`
73
+ * **Static Analysis Features**:
74
+ * `churn_score` — lines changed in the file (higher = more volatile)
75
+ * `complexity_score` — cyclomatic complexity proxy (higher = harder to review)
76
+ * `todo_score` — count of TODOs/FIXMEs (higher = more tech debt)
77
+ * `recency_score` — how recently the file was modified (higher = more recent)
78
+ * **Limits**: `review_budget` and `files_flagged`
79
+ * **Terminal Metrics**: `precision`, `recall`, `f1_score`, `true_positives`, `false_positives`, `false_negatives`, `true_negatives`
80
+
81
+ #### 🎯 The Three Tasks (Difficulty Tiers)
82
+ We've partitioned the environment into three distinct difficulty tasks, scaling gracefully by the size of the repository logic the agent needs to parse over its fixed review budget:
83
+ 1. **Easy (`difficulty="easy"`)**: Small repositories and pull requests (≤ 15 files). The budget is relatively forgiving.
84
+ 2. **Medium (`difficulty="medium"`)**: Average-sized PRs (16-29 files) requiring more scrutiny.
85
+ 3. **Hard (`difficulty="hard"`)**: Large-scale patches (30+ files). The agent is strapped for budget and must be extremely selective about utilizing its flags.
86
+
87
+ Each task includes a **programmatic grader** that returns a score between 0.0 and 1.0 (the F1-score), combining precision and recall into a single metric. The grader is deterministic and reproducible.
88
+
89
+ ---
90
+
91
+ ## Setup & Running
92
+
93
+ **1. Install deps:**
94
  ```bash
95
  pip install openenv-core openai
96
  ```
97
 
98
+ **2. Spin up the FastAPI Server via Docker:**
99
  ```bash
100
  docker build -t codereviewenv .
101
+ docker run -p 7860:7860 codereviewenv
102
  ```
103
+ *(If you are viewing this on Hugging Face Spaces, the server is automatically running!)*
104
 
105
+ **3. Run the inference script:**
 
 
106
  ```bash
107
+ export OPENAI_API_KEY="your_api_key" # or export HF_TOKEN="your_hf_token"
108
  python inference.py
109
  ```
110
 
111
+ ---
112
 
113
+ ## The Agents (We built two!)
 
 
 
 
114
 
115
+ ### 1. The Zero-Shot LLM Baseline (`inference.py`)
116
+ This is the standard OpenEnv submission script required by the Hackathon. We wrote a wrapper that passes the environment state into an OpenAI-compatible LLM to see if a huge model can reason through the file stats to allocate its budget. It evaluates the environment sequentially over the **Easy**, **Medium**, and **Hard** tasks.
117
 
118
+ **Baseline Scores:**
 
 
 
 
119
 
120
+ | Difficulty | Model | F1-Score | Precision | Recall |
121
+ |-----------|-------|----------|-----------|--------|
122
+ | Easy | Qwen2.5-Coder-32B | ~0.15 | ~0.12 | ~0.25 |
123
+ | Medium | Qwen2.5-Coder-32B | ~0.10 | ~0.08 | ~0.18 |
124
+ | Hard | Qwen2.5-Coder-32B | ~0.08 | ~0.06 | ~0.15 |
125
 
126
+ These are zero-shot scores — the LLM has no training on this specific task, demonstrating there is real room for improvement via RL training.
127
 
128
+ ```bash
129
+ export OPENAI_API_KEY="your_api_key"
130
+ python inference.py
131
  ```
132
+
133
+ ### 2. The Native PyTorch Agent (`train_pytorch_agent.py`)
134
+ **Flex Warning.** LLMs are cool, but they struggle to implicitly understand strict mathematical bounds (like rationing a flag budget perfectly over exactly 29 files with precise asymmetric scoring). So, we went bare-metal.
135
+
136
+ We built a custom Deep Reinforcement Learning Agent using native PyTorch Policy Gradients (REINFORCE) to interface perfectly with the OpenEnv API. It iteratively converges to find the perfect risk/reward strategy.
137
+ ```bash
138
+ pip install torch
139
+ python train_pytorch_agent.py
 
 
140
  ```
141
 
142
+ ## Hackathon Repo Tour
143
+ * `Dockerfile` & `openenv.yaml`: The OpenEnv backend deployment wrappers
144
+ * `inference.py`: The mandatory LLM endpoint validation script
145
+ * `train_pytorch_agent.py`: Our custom PyTorch REINFORCE brain
146
+ * `/models.py`: Pydantic Models for Actions & Observations
147
+ * `/server/environment.py`: Where the magic reward mathematics happen
148
+ * `/data/`: The actual scraped CVE GitHub dataset
149
 
150
+ *MIT License. Thanks for checking it out!*
code_review_env.egg-info/requires.txt CHANGED
@@ -2,3 +2,9 @@ openenv-core>=0.2.0
2
  fastapi>=0.100.0
3
  uvicorn>=0.20.0
4
  pydantic>=2.0.0
 
 
 
 
 
 
 
2
  fastapi>=0.100.0
3
  uvicorn>=0.20.0
4
  pydantic>=2.0.0
5
+
6
+ [inference]
7
+ openai>=1.0.0
8
+
9
+ [train]
10
+ torch>=2.0.0
demo.py CHANGED
@@ -12,7 +12,7 @@ def run_episode(env, agent_fn, name, episode=None):
12
  if episode:
13
  orig = random.choice
14
  random.choice = lambda x: episode
15
- obs = env.reset()
16
  random.choice = orig
17
  else:
18
  obs = env.reset()
 
12
  if episode:
13
  orig = random.choice
14
  random.choice = lambda x: episode
15
+ obs = env.reset(difficulty="medium")
16
  random.choice = orig
17
  else:
18
  obs = env.reset()
inference.py CHANGED
@@ -11,7 +11,6 @@ API_BASE_URL = os.getenv("API_BASE_URL", "https://api-inference.huggingface.co/v
11
  MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-Coder-32B-Instruct")
12
  LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME", "codereviewenv")
13
 
14
- # HF_TOKEN is primary per sample inference.py, API_KEY as fallback
15
  API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
16
 
17
  if API_KEY is None:
 
11
  MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-Coder-32B-Instruct")
12
  LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME", "codereviewenv")
13
 
 
14
  API_KEY = os.getenv("HF_TOKEN") or os.getenv("API_KEY")
15
 
16
  if API_KEY is None:
models.py CHANGED
@@ -40,3 +40,4 @@ class CodeReviewState(State):
40
  files_flagged: int = 0
41
  correct_flags: int = 0
42
  review_budget: int = 0
 
 
40
  files_flagged: int = 0
41
  correct_flags: int = 0
42
  review_budget: int = 0
43
+ cumulative_reward: float = 0.0
openenv.yaml CHANGED
@@ -1,3 +1,8 @@
1
  name: code_review_env
2
  version: "1.0.0"
3
- description: RL env for code vulnerability triage using CVE data from NVD
 
 
 
 
 
 
1
  name: code_review_env
2
  version: "1.0.0"
3
+ description: RL env for triaging CVE vulnerabilities in code repos
4
+ entry_point: code_review_env.server.app:app
5
+ tags:
6
+ - openenv
7
+ - security
8
+ - code-review
pyproject.toml CHANGED
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
  [project]
6
  name = "code-review-env"
7
  version = "1.0.0"
8
- description = "RL environment for code vulnerability triage using real CVE data"
9
  readme = "README.md"
10
  requires-python = ">=3.10"
11
  license = {text = "MIT"}
@@ -16,8 +16,15 @@ dependencies = [
16
  "fastapi>=0.100.0",
17
  "uvicorn>=0.20.0",
18
  "pydantic>=2.0.0",
 
 
 
 
19
  "torch>=2.0.0",
20
  ]
 
 
 
21
 
22
  [tool.setuptools.packages.find]
23
  include = ["code_review_env*"]
 
5
  [project]
6
  name = "code-review-env"
7
  version = "1.0.0"
8
+ description = "RL env for CVE triage on real vulnerability data"
9
  readme = "README.md"
10
  requires-python = ">=3.10"
11
  license = {text = "MIT"}
 
16
  "fastapi>=0.100.0",
17
  "uvicorn>=0.20.0",
18
  "pydantic>=2.0.0",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ train = [
23
  "torch>=2.0.0",
24
  ]
25
+ inference = [
26
+ "openai>=1.0.0",
27
+ ]
28
 
29
  [tool.setuptools.packages.find]
30
  include = ["code_review_env*"]
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__/environment.cpython-312.pyc CHANGED
Binary files a/server/__pycache__/environment.cpython-312.pyc and b/server/__pycache__/environment.cpython-312.pyc differ
 
server/app.py CHANGED
@@ -6,7 +6,7 @@ app = create_fastapi_app(CodeReviewEnvironment, CodeReviewAction, CodeReviewObse
6
 
7
  @app.get("/")
8
  def root():
9
- return {"status": "success", "message": "CodeReviewEnv is active and ready for OpenEnv clients!"}
10
 
11
 
12
  def main():
 
6
 
7
  @app.get("/")
8
  def root():
9
+ return {"status": "ok", "env": "code_review"}
10
 
11
 
12
  def main():
server/environment.py CHANGED
@@ -73,7 +73,7 @@ class CodeReviewEnvironment(Environment):
73
  if difficulty not in ["easy", "medium", "hard"]:
74
  difficulty = random.choice(["easy", "medium", "hard"])
75
 
76
- # Filter by difficulty
77
  if difficulty == "easy":
78
  size_filter = lambda e: len(e["files"]) <= 15
79
  elif difficulty == "medium":
@@ -81,22 +81,19 @@ class CodeReviewEnvironment(Environment):
81
  else:
82
  size_filter = lambda e: len(e["files"]) >= 30
83
 
84
- # Strongly prefer episodes that actually contain bugs
85
- # without bugs F1 is always 0.0 regardless of agent behavior,
86
- # which would trip the "graders always return same score" disqualification.
87
  buggy_candidates = [e for e in BUGGY_EPISODES if size_filter(e)]
88
  if buggy_candidates:
89
  ep = random.choice(buggy_candidates)
90
  else:
91
- # Fallback: pick any episode matching size, then inject synthetic bugs
92
  all_candidates = [e for e in EPISODES if size_filter(e)]
93
  if not all_candidates:
94
  all_candidates = BUGGY_EPISODES if BUGGY_EPISODES else EPISODES
95
  ep = random.choice(all_candidates)
96
 
97
- # If the chosen episode has no bugs, inject some
98
  if ep["total_bugs"] == 0:
99
- ep = dict(ep) # shallow copy to avoid mutating global
100
  files = [dict(f) for f in ep["files"]]
101
  n_inject = max(1, len(files) // 8)
102
  targets = random.sample(range(len(files)), min(n_inject, len(files)))
 
73
  if difficulty not in ["easy", "medium", "hard"]:
74
  difficulty = random.choice(["easy", "medium", "hard"])
75
 
76
+
77
  if difficulty == "easy":
78
  size_filter = lambda e: len(e["files"]) <= 15
79
  elif difficulty == "medium":
 
81
  else:
82
  size_filter = lambda e: len(e["files"]) >= 30
83
 
84
+ # need episodes w/ bugs or f1 is stuck at 0
 
 
85
  buggy_candidates = [e for e in BUGGY_EPISODES if size_filter(e)]
86
  if buggy_candidates:
87
  ep = random.choice(buggy_candidates)
88
  else:
89
+ # fallback: any matching ep, add synthetic bugs if clean
90
  all_candidates = [e for e in EPISODES if size_filter(e)]
91
  if not all_candidates:
92
  all_candidates = BUGGY_EPISODES if BUGGY_EPISODES else EPISODES
93
  ep = random.choice(all_candidates)
94
 
 
95
  if ep["total_bugs"] == 0:
96
+ ep = dict(ep)
97
  files = [dict(f) for f in ep["files"]]
98
  n_inject = max(1, len(files) // 8)
99
  targets = random.sample(range(len(files)), min(n_inject, len(files)))
train_pytorch_agent.py CHANGED
@@ -45,7 +45,7 @@ def main():
45
  optimizer = optim.Adam(policy_net.parameters(), lr=learning_rate)
46
 
47
  num_episodes = 50
48
- print(f"Initializing PyTorch REINFORCE Agent natively on CodeReviewEnv for {num_episodes} episodes...")
49
 
50
  env = CodeReviewEnvironment()
51
 
@@ -104,7 +104,7 @@ def main():
104
  f"Reward: {sum(rewards):.1f} | "
105
  f"F1: {obs.f1_score:.2f} (P: {obs.precision:.2f}, R: {obs.recall:.2f})")
106
 
107
- print("\nTraining complete! PyTorch internal weights updated.")
108
 
109
  if __name__ == "__main__":
110
  main()
 
45
  optimizer = optim.Adam(policy_net.parameters(), lr=learning_rate)
46
 
47
  num_episodes = 50
48
+ print(f"training for {num_episodes} episodes")
49
 
50
  env = CodeReviewEnvironment()
51
 
 
104
  f"Reward: {sum(rewards):.1f} | "
105
  f"F1: {obs.f1_score:.2f} (P: {obs.precision:.2f}, R: {obs.recall:.2f})")
106
 
107
+ print("\ndone.")
108
 
109
  if __name__ == "__main__":
110
  main()