tanaymitra98 commited on
Commit
b214779
·
0 Parent(s):

Initial commit: MetaXScalar project

Browse files
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt /app/requirements.txt
6
+ RUN pip install --no-cache-dir -r /app/requirements.txt
7
+
8
+ COPY . /app
9
+
10
+ ENV PYTHONUNBUFFERED=1
11
+
12
+ EXPOSE 7860
13
+
14
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
HACKATHON_GUIDELINES.md ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Meta x PyTorch Hackathon - Round 1 Guidelines
2
+
3
+ ## Overview
4
+
5
+ - **Event**: Meta x PyTorch Hackathon by Scaler School of Technology
6
+ - **Theme**: Build OpenEnv environments (Reinforcement Learning)
7
+ - **Registration**: 14th March - 3rd April
8
+ - **Round 1**: 25th March - 8th April
9
+ - **Submission Window Opens**: 28th March
10
+ - **Finale**: 25th-26th April
11
+ - **Submission Deadline**: 8th April 2026, 11:59 PM (confirm timezone on dashboard)
12
+
13
+ ---
14
+
15
+ ## Team Structure
16
+
17
+ - **Solo**: Compete individually (locked for Round 1 only)
18
+ - **Team**: 2-3 members. Only team lead fills the team form.
19
+ - Once confirmed, teams cannot be changed.
20
+
21
+ ---
22
+
23
+ ## Round 1 Problem Statement
24
+
25
+ Build a complete, real-world OpenEnv environment that an AI agent can learn from through the standard `step()` / `reset()` / `state()` API.
26
+
27
+ ### Key Requirements
28
+
29
+ 1. **Must simulate a real-world task** (not games or toys)
30
+ 2. **Implement full OpenEnv spec**: typed models, `step()/reset()/state()`, `openenv.yaml`
31
+ 3. **Minimum 3 tasks** with agent graders (easy → medium → hard, scores/reward 0.0–1.0)
32
+ 4. **Meaningful reward function** with partial progress signals
33
+ 5. **Baseline inference script** with reproducible scores
34
+ 6. **Deploy to Hugging Face Spaces** + working Dockerfile
35
+ 7. **README** with environment description, action/observation spaces, setup instructions
36
+
37
+ ---
38
+
39
+ ## Evaluation Criteria
40
+
41
+ ### Pre-Submission Checklist (All Must Pass)
42
+
43
+ | Criteria | Description |
44
+ |----------|-------------|
45
+ | **HF Space deploys** | Automated ping to Space URL must return 200 and respond to `reset()` |
46
+ | **OpenEnv spec compliance** | Validate `openenv.yaml`, typed models, `step()/reset()/state()` endpoints |
47
+ | **Dockerfile builds** | Automated docker build on submitted repo |
48
+ | **Baseline reproduces** | Run inference script — must complete without error and produce scores |
49
+ | **3+ tasks with graders** | Enumerate tasks, run each grader, verify scores/reward in 0.0–1.0 range |
50
+
51
+ ---
52
+
53
+ ## Mandatory Additional Instructions
54
+
55
+ ### Environment Variables (Must be defined)
56
+
57
+ ```bash
58
+ API_BASE_URL # The API endpoint for the LLM
59
+ MODEL_NAME # The model identifier to use for inference
60
+ HF_TOKEN # Your Hugging Face API key
61
+ ```
62
+
63
+ ### Inference Script Requirements
64
+
65
+ - **Filename**: Must be named `inference.py` in the root directory
66
+ - **LLM Client**: Must use OpenAI Client for all LLM calls
67
+ - **Logging Format**: Must emit structured stdout logs following `[START]`, `[STEP]`, and `[END]` format (field names, ordering, and formatting are strict)
68
+
69
+ ### Infrastructure Restrictions
70
+
71
+ - Runtime of inference script should be less than **20 minutes**
72
+ - Must work on a machine with **vCPU=2, memory=8GB**
73
+
74
+ ---
75
+
76
+ ## Quick Checklist (Must-Haves)
77
+
78
+ - HF Space returns 200 and responds to `reset()`
79
+ - `openenv.yaml` validates; `step()/reset()/state()` endpoints respond correctly
80
+ - Dockerfile builds in CI
81
+ - `inference.py` runs end-to-end and produces scores
82
+ - 3+ tasks with graders; reward in 0.0–1.0 range
83
+ - OpenAI client used for all LLM calls; logs follow strict `[START]/[STEP]/[END]` format
84
+
85
+ ---
86
+
87
+ ## Preparatory Course (4 Modules ~3.5 hours)
88
+
89
+ | Module | Title | Duration |
90
+ |--------|-------|----------|
91
+ | 1 | Why OpenEnv? | 45 min |
92
+ | 2 | Using Existing Environments | 50 min |
93
+ | 3 | Deploying Environments | 45 min |
94
+ | 4 | Building Your Own Environment | 60 min |
95
+
96
+ **Note**: Each module - read the README first, then open the notebook in Colab. No local setup needed.
97
+
98
+ [Course Repository](https://github.com/raun/openenv-course/tree/main)
99
+
100
+ ---
101
+
102
+ ## How to Submit
103
+
104
+ 1. Complete Step 1 (Team/Solo selection)
105
+ 2. Build your OpenEnv environment
106
+ 3. Deploy to Hugging Face Spaces
107
+ 4. Run pre-submission validation script
108
+ 5. Submit via dashboard (only team leaders can submit)
109
+
110
+ ---
111
+
112
+ ## What Happens After Round 1
113
+
114
+ - Results announced: 10th April
115
+ - Finale: 25th-26th April
116
+
117
+ ---
118
+
119
+ ## Need Help?
120
+
121
+ - **Email**: help_openenvhackathon@scaler.com
122
+ - **Discord**: Join the community for announcements, mentor access, and team matching
123
+ - **Discord Link**: https://discord.gg/Dedhy5pkWD
124
+
125
+ ---
126
+
127
+ ## Example Problem Statement Format
128
+
129
+ > "Build a real-world task environment (e.g., incident triage or logistics scheduling) with clearly defined tasks, automated graders, and reward logic using the OpenEnv framework."
130
+
131
+ ### Expected Deliverables:
132
+ - Create an environment an AI agent can interact with
133
+ - Define tasks with increasing difficulty
134
+ - Write graders that verify task completion
135
+ - Define reward logic for scoring
136
+ - Package using OpenEnv for automated evaluation
137
+
138
+ ### Evaluation Areas:
139
+ - **Runtime correctness**: Runs without errors
140
+ - **Interface compliance**: Follows OpenEnv standard
141
+ - **Task design**: Clear, realistic, testable
142
+ - **Grading logic**: Reward system makes sense
143
+
144
+ ---
145
+
146
+ ## Resources
147
+
148
+ - [OpenEnv Course Repository](https://github.com/raun/openenv-course/tree/main)
149
+ - [Join Discord Community](https://discord.gg/Dedhy5pkWD)
150
+ - [Contact Support](mailto:help_openenvhackathon@scaler.com)
151
+
152
+ ---
153
+
154
+ *Last Updated: April 3, 2026*
README.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SolidityGuard
2
+
3
+ SolidityGuard is an OpenEnv RL environment that trains agents to review Solidity smart contracts for best practices, gas optimizations, and security vulnerabilities.
4
+
5
+ ## Quick Start
6
+
7
+ ### Requirements
8
+ - Python 3.11+
9
+ - `API_BASE_URL`, `MODEL_NAME`, `HF_TOKEN` set in the environment
10
+
11
+ ### Install
12
+ ```bash
13
+ pip install -r requirements.txt
14
+ ```
15
+
16
+ ### Run Inference
17
+ ```bash
18
+ python inference.py
19
+ ```
20
+
21
+ ### Run API Server
22
+ ```bash
23
+ uvicorn app:app --host 0.0.0.0 --port 7860
24
+ ```
25
+
26
+ ### Expected Output
27
+ Structured logs with `[START]`, `[STEP]`, and `[END]` tags. The final score is reported in the `[END]` log.
28
+
29
+ ## Environment Overview
30
+
31
+ ### Observation
32
+ - `source_code`: Solidity code string
33
+ - `metadata`: contract name, compiler version, file path
34
+ - `task_id`: active task
35
+
36
+ ### Action
37
+ JSON array of findings:
38
+ ```json
39
+ [
40
+ {
41
+ "issue_type": "reentrancy",
42
+ "line_number": 13,
43
+ "description": "State updated after external call",
44
+ "severity": "Critical"
45
+ }
46
+ ]
47
+ ```
48
+
49
+ ### Tasks
50
+ - Task 1: Best practices and syntax
51
+ - Task 2: Gas optimization
52
+ - Task 3: Security vulnerabilities
53
+
54
+ ## Files
55
+ - `openenv.yaml`: Environment spec
56
+ - `environment.py`: Core env logic (`reset/step/state`)
57
+ - `graders.py`: Reward logic and grading
58
+ - `data/manifest.json`: Dataset manifest
59
+ - `inference.py`: Baseline runner and logging
60
+ - `app.py`: FastAPI endpoints for reset/step/state
61
+ - `Dockerfile`: Container build
62
+
63
+ ## Notes
64
+ - Runtime should stay under 20 minutes on 2 vCPU / 8 GB.
65
+ - Docker build must succeed for submission.
app.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from fastapi import FastAPI, HTTPException
6
+ from pydantic import BaseModel, Field
7
+
8
+ from environment import SolidityGuardEnv
9
+
10
+
11
+ app = FastAPI(title="SolidityGuard")
12
+ env = SolidityGuardEnv()
13
+
14
+
15
+ class ResetRequest(BaseModel):
16
+ task_id: Optional[str] = Field(default=None)
17
+
18
+
19
+ class StepRequest(BaseModel):
20
+ action: List[Dict[str, Any]]
21
+
22
+
23
+ @app.get("/health")
24
+ def health() -> Dict[str, str]:
25
+ return {"status": "ok"}
26
+
27
+
28
+ @app.post("/reset")
29
+ def reset(request: ResetRequest) -> Dict[str, Any]:
30
+ try:
31
+ return env.reset(task_id=request.task_id)
32
+ except Exception as exc:
33
+ raise HTTPException(status_code=400, detail=str(exc))
34
+
35
+
36
+ @app.post("/step")
37
+ def step(request: StepRequest) -> Dict[str, Any]:
38
+ try:
39
+ return env.step(request.action)
40
+ except Exception as exc:
41
+ raise HTTPException(status_code=400, detail=str(exc))
42
+
43
+
44
+ @app.get("/state")
45
+ def state() -> Dict[str, Any]:
46
+ try:
47
+ return env.state()
48
+ except Exception as exc:
49
+ raise HTTPException(status_code=400, detail=str(exc))
data/manifest.json ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "id": "t1_sample_1",
4
+ "task_id": "task_1_best_practices",
5
+ "source_path": "data/samples/task1/missing_spdx.sol",
6
+ "metadata": {
7
+ "contract_name": "NoSpdx",
8
+ "compiler_version": "0.7.6",
9
+ "file_path": "missing_spdx.sol"
10
+ },
11
+ "labels": [
12
+ {
13
+ "issue_type": "missing_spdx",
14
+ "line_number": 1,
15
+ "description": "Missing SPDX license identifier",
16
+ "severity": "Low"
17
+ },
18
+ {
19
+ "issue_type": "old_compiler_version",
20
+ "line_number": 2,
21
+ "description": "Compiler version below 0.8.x",
22
+ "severity": "Low"
23
+ }
24
+ ]
25
+ },
26
+ {
27
+ "id": "t1_sample_2",
28
+ "task_id": "task_1_best_practices",
29
+ "source_path": "data/samples/task1/missing_natspec.sol",
30
+ "metadata": {
31
+ "contract_name": "NoNatSpec",
32
+ "compiler_version": "0.8.13",
33
+ "file_path": "missing_natspec.sol"
34
+ },
35
+ "labels": [
36
+ {
37
+ "issue_type": "missing_natspec",
38
+ "line_number": 6,
39
+ "description": "Public function missing NatSpec comment",
40
+ "severity": "Low"
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ "id": "t1_sample_3",
46
+ "task_id": "task_1_best_practices",
47
+ "source_path": "data/samples/task1/deprecated_constructor.sol",
48
+ "metadata": {
49
+ "contract_name": "LegacyConstructor",
50
+ "compiler_version": "0.4.24",
51
+ "file_path": "deprecated_constructor.sol"
52
+ },
53
+ "labels": [
54
+ {
55
+ "issue_type": "deprecated_constructor",
56
+ "line_number": 6,
57
+ "description": "Constructor uses deprecated syntax",
58
+ "severity": "Low"
59
+ },
60
+ {
61
+ "issue_type": "old_compiler_version",
62
+ "line_number": 2,
63
+ "description": "Compiler version below 0.8.x",
64
+ "severity": "Low"
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ "id": "t2_sample_1",
70
+ "task_id": "task_2_gas_optimization",
71
+ "source_path": "data/samples/task2/inefficient_loop.sol",
72
+ "metadata": {
73
+ "contract_name": "LoopGas",
74
+ "compiler_version": "0.8.17",
75
+ "file_path": "inefficient_loop.sol"
76
+ },
77
+ "labels": [
78
+ {
79
+ "issue_type": "unbounded_loop",
80
+ "line_number": 10,
81
+ "description": "Loop uses dynamic array length without bounds",
82
+ "severity": "Medium"
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "id": "t2_sample_2",
88
+ "task_id": "task_2_gas_optimization",
89
+ "source_path": "data/samples/task2/redundant_storage.sol",
90
+ "metadata": {
91
+ "contract_name": "StorageGas",
92
+ "compiler_version": "0.8.19",
93
+ "file_path": "redundant_storage.sol"
94
+ },
95
+ "labels": [
96
+ {
97
+ "issue_type": "redundant_storage_read",
98
+ "line_number": 12,
99
+ "description": "Repeated storage reads could be cached",
100
+ "severity": "Medium"
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ "id": "t2_sample_3",
106
+ "task_id": "task_2_gas_optimization",
107
+ "source_path": "data/samples/task2/require_string.sol",
108
+ "metadata": {
109
+ "contract_name": "RequireString",
110
+ "compiler_version": "0.8.18",
111
+ "file_path": "require_string.sol"
112
+ },
113
+ "labels": [
114
+ {
115
+ "issue_type": "custom_error_missing",
116
+ "line_number": 6,
117
+ "description": "Custom errors are preferred over require strings",
118
+ "severity": "Low"
119
+ }
120
+ ]
121
+ },
122
+ {
123
+ "id": "t3_sample_1",
124
+ "task_id": "task_3_security",
125
+ "source_path": "data/samples/task3/reentrancy.sol",
126
+ "metadata": {
127
+ "contract_name": "Reentry",
128
+ "compiler_version": "0.8.20",
129
+ "file_path": "reentrancy.sol"
130
+ },
131
+ "labels": [
132
+ {
133
+ "issue_type": "reentrancy",
134
+ "line_number": 13,
135
+ "description": "State update after external call allows reentrancy",
136
+ "severity": "Critical"
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ "id": "t3_sample_2",
142
+ "task_id": "task_3_security",
143
+ "source_path": "data/samples/task3/missing_access_control.sol",
144
+ "metadata": {
145
+ "contract_name": "Admin",
146
+ "compiler_version": "0.8.20",
147
+ "file_path": "missing_access_control.sol"
148
+ },
149
+ "labels": [
150
+ {
151
+ "issue_type": "missing_access_control",
152
+ "line_number": 9,
153
+ "description": "Sensitive function lacks access control",
154
+ "severity": "Critical"
155
+ }
156
+ ]
157
+ },
158
+ {
159
+ "id": "t3_sample_3",
160
+ "task_id": "task_3_security",
161
+ "source_path": "data/samples/task3/tx_origin_auth.sol",
162
+ "metadata": {
163
+ "contract_name": "OriginAuth",
164
+ "compiler_version": "0.8.20",
165
+ "file_path": "tx_origin_auth.sol"
166
+ },
167
+ "labels": [
168
+ {
169
+ "issue_type": "tx_origin_auth",
170
+ "line_number": 11,
171
+ "description": "Authorization uses tx.origin",
172
+ "severity": "Critical"
173
+ }
174
+ ]
175
+ }
176
+ ]
data/samples/task1/deprecated_constructor.sol ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.4.24;
3
+
4
+ contract LegacyConstructor {
5
+ uint256 public value;
6
+
7
+ function LegacyConstructor(uint256 initial) public {
8
+ value = initial;
9
+ }
10
+ }
data/samples/task1/missing_natspec.sol ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ contract NoNatSpec {
5
+ uint256 public total;
6
+
7
+ function add(uint256 value) public {
8
+ total += value;
9
+ }
10
+ }
data/samples/task1/missing_spdx.sol ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ pragma solidity ^0.7.6;
2
+
3
+ contract NoSpdx {
4
+ uint256 public count;
5
+
6
+ function increment() public {
7
+ count += 1;
8
+ }
9
+ }
data/samples/task2/inefficient_loop.sol ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.17;
3
+
4
+ contract LoopGas {
5
+ uint256[] public values;
6
+
7
+ function sum() public view returns (uint256) {
8
+ uint256 total = 0;
9
+ for (uint256 i = 0; i < values.length; i++) {
10
+ total += values[i];
11
+ }
12
+ return total;
13
+ }
14
+ }
data/samples/task2/redundant_storage.sol ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ contract StorageGas {
5
+ uint256 public price;
6
+ uint256 public fee;
7
+
8
+ function totalCost(uint256 quantity) public view returns (uint256) {
9
+ uint256 cost = price * quantity;
10
+ cost = cost + fee;
11
+ cost = cost + fee;
12
+ return cost;
13
+ }
14
+ }
data/samples/task2/require_string.sol ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.18;
3
+
4
+ contract RequireString {
5
+ function update(uint256 amount) public pure returns (uint256) {
6
+ require(amount > 0, "amount must be positive");
7
+ return amount;
8
+ }
9
+ }
data/samples/task3/missing_access_control.sol ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ contract Admin {
5
+ mapping(address => bool) public admins;
6
+
7
+ function setAdmin(address account, bool status) public {
8
+ admins[account] = status;
9
+ }
10
+ }
data/samples/task3/reentrancy.sol ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ contract Reentry {
5
+ mapping(address => uint256) public balances;
6
+
7
+ function deposit() public payable {
8
+ balances[msg.sender] += msg.value;
9
+ }
10
+
11
+ function withdraw() public {
12
+ uint256 amount = balances[msg.sender];
13
+ require(amount > 0);
14
+ (bool success, ) = msg.sender.call{value: amount}("");
15
+ require(success);
16
+ balances[msg.sender] = 0;
17
+ }
18
+ }
data/samples/task3/tx_origin_auth.sol ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ contract OriginAuth {
5
+ address public owner;
6
+
7
+ constructor() {
8
+ owner = msg.sender;
9
+ }
10
+
11
+ function withdraw(uint256 amount) public {
12
+ require(tx.origin == owner);
13
+ payable(msg.sender).transfer(amount);
14
+ }
15
+ }
environment.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ from dataclasses import dataclass
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ from graders import grade_action
9
+
10
+
11
+ @dataclass
12
+ class EnvironmentState:
13
+ task_id: str
14
+ step_count: int
15
+ max_steps: int
16
+ score_so_far: float
17
+ done: bool
18
+
19
+
20
+ class SolidityGuardEnv:
21
+ def __init__(self, data_path: str = "data/manifest.json") -> None:
22
+ self.data_path = data_path
23
+ self._load_manifest()
24
+ self._index = 0
25
+ self._current_sample: Optional[Dict[str, Any]] = None
26
+ self._state: Optional[EnvironmentState] = None
27
+
28
+ def _load_manifest(self) -> None:
29
+ if not os.path.exists(self.data_path):
30
+ raise FileNotFoundError(f"Manifest not found: {self.data_path}")
31
+ with open(self.data_path, "r", encoding="utf-8") as handle:
32
+ self._manifest = json.load(handle)
33
+ if not isinstance(self._manifest, list) or not self._manifest:
34
+ raise ValueError("Manifest must be a non-empty list")
35
+
36
+ def reset(self, task_id: Optional[str] = None) -> Dict[str, Any]:
37
+ if task_id:
38
+ candidates = [item for item in self._manifest if item["task_id"] == task_id]
39
+ else:
40
+ candidates = self._manifest
41
+
42
+ if not candidates:
43
+ raise ValueError("No samples available for the requested task")
44
+
45
+ self._current_sample = candidates[self._index % len(candidates)]
46
+ self._index += 1
47
+
48
+ source_path = self._current_sample["source_path"]
49
+ with open(source_path, "r", encoding="utf-8") as handle:
50
+ source_code = handle.read()
51
+
52
+ observation = {
53
+ "source_code": source_code,
54
+ "metadata": self._current_sample.get("metadata", {}),
55
+ "task_id": self._current_sample["task_id"],
56
+ }
57
+
58
+ self._state = EnvironmentState(
59
+ task_id=self._current_sample["task_id"],
60
+ step_count=0,
61
+ max_steps=1,
62
+ score_so_far=0.0,
63
+ done=False,
64
+ )
65
+
66
+ return observation
67
+
68
+ def step(self, action: List[Dict[str, Any]]) -> Dict[str, Any]:
69
+ if self._current_sample is None or self._state is None:
70
+ raise RuntimeError("Call reset() before step().")
71
+
72
+ if self._state.done:
73
+ return {
74
+ "reward": self._state.score_so_far,
75
+ "done": True,
76
+ "details": {"message": "Episode already completed"},
77
+ }
78
+
79
+ expected = self._current_sample.get("labels", [])
80
+ reward, details = grade_action(action, expected)
81
+
82
+ self._state.step_count += 1
83
+ self._state.score_so_far = reward
84
+ self._state.done = True
85
+
86
+ return {
87
+ "reward": reward,
88
+ "done": True,
89
+ "details": details,
90
+ }
91
+
92
+ def state(self) -> Dict[str, Any]:
93
+ if self._state is None:
94
+ raise RuntimeError("Call reset() before state().")
95
+ return {
96
+ "task_id": self._state.task_id,
97
+ "step_count": self._state.step_count,
98
+ "max_steps": self._state.max_steps,
99
+ "score_so_far": self._state.score_so_far,
100
+ "done": self._state.done,
101
+ }
graders.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, List, Optional, Tuple
5
+
6
+
7
+ @dataclass(frozen=True)
8
+ class Issue:
9
+ issue_type: str
10
+ line_number: Optional[int]
11
+ severity: str
12
+
13
+
14
+ def _normalize_issue(issue: Dict[str, Any]) -> Issue:
15
+ return Issue(
16
+ issue_type=str(issue.get("issue_type", "")).strip(),
17
+ line_number=issue.get("line_number"),
18
+ severity=str(issue.get("severity", "")).strip(),
19
+ )
20
+
21
+
22
+ def _match_issue(pred: Issue, expected: Issue) -> bool:
23
+ if pred.issue_type.lower() != expected.issue_type.lower():
24
+ return False
25
+ if pred.severity.lower() != expected.severity.lower():
26
+ return False
27
+ return True
28
+
29
+
30
+ def _line_bonus(pred_line: Optional[int], exp_line: Optional[int]) -> float:
31
+ if pred_line is None or exp_line is None:
32
+ return 0.0
33
+ diff = abs(pred_line - exp_line)
34
+ if diff == 0:
35
+ return 0.2
36
+ if diff <= 2:
37
+ return 0.1
38
+ return 0.0
39
+
40
+
41
+ def grade_action(action: List[Dict[str, Any]], expected: List[Dict[str, Any]]) -> Tuple[float, Dict[str, Any]]:
42
+ expected_issues = [_normalize_issue(item) for item in expected]
43
+ predicted_issues = [_normalize_issue(item) for item in action]
44
+
45
+ matched = 0
46
+ line_bonus_total = 0.0
47
+ expected_used = [False] * len(expected_issues)
48
+
49
+ for pred in predicted_issues:
50
+ found = False
51
+ for idx, exp in enumerate(expected_issues):
52
+ if expected_used[idx]:
53
+ continue
54
+ if _match_issue(pred, exp):
55
+ expected_used[idx] = True
56
+ matched += 1
57
+ line_bonus_total += _line_bonus(pred.line_number, exp.line_number)
58
+ found = True
59
+ break
60
+ if not found:
61
+ continue
62
+
63
+ expected_count = max(len(expected_issues), 1)
64
+ base_score = matched / expected_count
65
+ false_positives = max(len(predicted_issues) - matched, 0)
66
+ fp_penalty = 0.05 * false_positives
67
+
68
+ score = base_score * 0.8 + min(line_bonus_total, 0.2) - fp_penalty
69
+ score = max(min(score, 1.0), 0.0)
70
+
71
+ details = {
72
+ "matched": matched,
73
+ "expected": len(expected_issues),
74
+ "false_positives": false_positives,
75
+ "line_bonus": round(min(line_bonus_total, 0.2), 3),
76
+ "score": round(score, 4),
77
+ }
78
+ return score, details
idea.md ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SolidityGuard - OpenEnv RL Environment
2
+
3
+ ## Project Title
4
+ **SolidityGuard** - An OpenEnv RL Environment for Smart Contract Security Review
5
+
6
+ ## Problem Statement
7
+ Build an AI agent that learns to review Solidity smart contracts for security vulnerabilities, gas optimization issues, and best practices using the OpenEnv framework.
8
+
9
+ ## Why This Project?
10
+ - Real-world utility: Smart contracts hold billions in crypto assets
11
+ - Unique & differentiated: Most RL envs are generic code review, Solidity is specialized
12
+ - Measurable success: Graders can verify against known vulnerability patterns
13
+ - High impact: Automated security review is in high demand in Web3
14
+
15
+ ---
16
+
17
+ ## Environment Design
18
+
19
+ ### Observation Space
20
+ - **Input**: Solidity source code (string)
21
+ - **Metadata**: Contract name, compiler version, file path
22
+
23
+ ### Action Space
24
+ - **Output**: Review comments with severity levels (Critical/Medium/Low/Info)
25
+ - **Format**: JSON with `issue_type`, `line_number`, `description`, `severity`
26
+
27
+ ---
28
+
29
+ ## 3 Tasks (Difficulty Levels)
30
+
31
+ | Task | Difficulty | Focus Area | Examples | Max Score |
32
+ |------|------------|------------|----------|-----------|
33
+ | Task 1 | Easy | Syntax & Best Practices | Missing SPDX license, old compiler version, missing NatSpec | 0.33 |
34
+ | Task 2 | Medium | Gas Optimization | Unchecked external calls, inefficient loops, redundant storage reads | 0.33 |
35
+ | Task 3 | Hard | Security Vulnerabilities | Reentrancy, integer overflow, access control issues, tx.origin usage | 0.34 |
36
+
37
+ ---
38
+
39
+ ## Grading System
40
+
41
+ ### Task 1: Syntax & Best Practices (0-0.33)
42
+ Grader checks for:
43
+ - [ ] SPDX license identifier present
44
+ - [ ] Compiler version specified (>=0.8.0)
45
+ - [ ] NatSpec comments on public functions
46
+ - [ ] No deprecated Solidity features
47
+
48
+ ### Task 2: Gas Optimization (0-0.33)
49
+ Grader checks for:
50
+ - [ ] Cache storage variables in memory where possible
51
+ - [ ] Use of custom errors instead of require strings
52
+ - [ ] Loop optimization (avoid unbounded loops)
53
+ - [ ] Unchecked external call handling
54
+
55
+ ### Task 3: Security Vulnerabilities (0-0.34)
56
+ Grader checks for:
57
+ - [ ] Reentrancy guards on sensitive functions
58
+ - [ ] Integer overflow/underflow protection
59
+ - [ ] Access control on sensitive functions
60
+ - [ ] Safe usage of low-level calls
61
+ - [ ] No tx.origin usage for authorization
62
+
63
+ ---
64
+
65
+ ## Reward Function
66
+
67
+ - **Partial Progress**: Points awarded for each correct issue detected
68
+ - **Penalty**: Points deducted for false positives (wrongly reported issues)
69
+ - **Explanation Bonus**: Extra points for accurate line numbers and helpful descriptions
70
+ - **Final Score**: 0.0 to 1.0 scale
71
+
72
+ ---
73
+
74
+ ## Tech Stack
75
+
76
+ - Python 3.x
77
+ - OpenEnv framework
78
+ - OpenAI Client for LLM inference
79
+ - Hugging Face Spaces + Docker
80
+ - Environment variables: `API_BASE_URL`, `MODEL_NAME`, `HF_TOKEN`
81
+
82
+ ---
83
+
84
+ ## Deliverables
85
+
86
+ 1. `openenv.yaml` - Environment specification
87
+ 2. `environment.py` - Main env implementation with step/reset/state
88
+ 3. `inference.py` - LLM inference script with [START]/[STEP]/[END] logging
89
+ 4. `Dockerfile` - Container configuration
90
+ 5. `README.md` - Setup instructions and documentation
91
+ 6. Deployed on Hugging Face Spaces
92
+
93
+ ---
94
+
95
+ ## Timeline
96
+
97
+ | Day | Task |
98
+ |-----|------|
99
+ | Day 1 | Setup + study OpenEnv templates |
100
+ | Day 2 | Design env spec + grader functions |
101
+ | Day 3 | Implement step/reset/state methods |
102
+ | Day 4 | Create inference.py with logging |
103
+ | Day 5 | Deploy to HF Spaces + validate |
104
+ | Day 6 | Polish + write README |
105
+ | Day 7 | Final validation + submit (Deadline: 8th April) |
106
+
107
+ ---
108
+
109
+ ## Production-Ready Execution Plan
110
+
111
+ ### Objective
112
+ Build **SolidityGuard**, a compliant OpenEnv RL environment where an agent audits Solidity contracts for security, gas optimization, and best practices. Must pass all hackathon validators: OpenEnv spec, graders, Docker build, Hugging Face Space, and reproducible `inference.py` with strict logging.
113
+
114
+ ### Scope & Constraints
115
+ - Real-world task; no games or toy problems
116
+ - Must implement OpenEnv spec: typed models + `reset()/step()/state()` + `openenv.yaml`
117
+ - Must include 3 tasks (easy/medium/hard) with graders and rewards in 0.0–1.0
118
+ - `inference.py` must exist in repo root, use OpenAI client, and emit strict `[START]/[STEP]/[END]` logs
119
+ - Runtime < 20 minutes on 2 vCPU / 8 GB
120
+ - Deploy to Hugging Face Spaces + Dockerfile must build
121
+
122
+ ### Environment Design Summary
123
+
124
+ **Observation**
125
+ - `source_code: str`
126
+ - `metadata`: contract name, compiler version, file path (optional)
127
+ - `task_id`: which task is active
128
+
129
+ **Action**
130
+ - JSON list of findings:
131
+ - `issue_type` (string enum)
132
+ - `line_number` (int or null)
133
+ - `description` (string)
134
+ - `severity` (Critical/Medium/Low/Info)
135
+
136
+ **State**
137
+ - `task_id`
138
+ - `step_count`
139
+ - `max_steps`
140
+ - `score_so_far`
141
+ - `done`
142
+
143
+ ### Task Definitions (3 Levels)
144
+
145
+ **Task 1 (Easy): Best Practices / Syntax**
146
+ - SPDX license presence
147
+ - compiler version >= 0.8.x
148
+ - NatSpec for public functions
149
+ - deprecated patterns detection
150
+
151
+ **Task 2 (Medium): Gas Optimization**
152
+ - cache storage vars
153
+ - custom errors instead of require strings
154
+ - avoid unbounded loops
155
+ - safe/efficient external calls
156
+
157
+ **Task 3 (Hard): Security Vulnerabilities**
158
+ - reentrancy detection
159
+ - missing access control
160
+ - unsafe low-level calls
161
+ - no `tx.origin` auth
162
+
163
+ ### Grader Logic
164
+ - Partial credit per correct finding
165
+ - Penalty for false positives
166
+ - Bonus for accurate line numbers (bounded)
167
+ - Normalize per task to 0.0–1.0
168
+ - Aggregate score across tasks
169
+
170
+ ### Dataset Plan
171
+ - 12–20 Solidity snippets total (4–6 per task)
172
+ - Each snippet has a ground-truth label set for issues
173
+ - Mix of clean and vulnerable examples
174
+ - Add control samples with no issues to penalize false positives
175
+
176
+ ### Implementation Plan (Step-By-Step)
177
+
178
+ **Phase 0 — Scope Lock (0.5 day)**
179
+ - Freeze task rubrics
180
+ - Lock dataset size + sourcing method
181
+ - Define baseline score targets
182
+
183
+ **Phase 1 — Spec & Schema (1 day)**
184
+ - Define typed models for observation/action/state
185
+ - Draft `openenv.yaml` with tasks and endpoints
186
+ - Validate schema alignment
187
+
188
+ **Phase 2 — Environment Core (1–2 days)**
189
+ - Implement `reset()` to provide a task-specific sample
190
+ - Implement `step()` to grade action JSON and produce reward
191
+ - Implement `state()` to surface current status
192
+ - Ensure deterministic responses
193
+
194
+ **Phase 3 — Graders + Reward (1–2 days)**
195
+ - Implement task graders
196
+ - Add partial credit and penalty logic
197
+ - Normalize to 0–1
198
+ - Unit-test grader outputs
199
+
200
+ **Phase 4 — Inference Pipeline (1 day)**
201
+ - Implement `inference.py`
202
+ - Use OpenAI client with `API_BASE_URL`, `MODEL_NAME`, `HF_TOKEN`
203
+ - Emit strict `[START]/[STEP]/[END]` logs with required fields
204
+ - Ensure runtime < 20 min
205
+
206
+ **Phase 5 — Packaging & Deployment (1 day)**
207
+ - Dockerfile builds and runs inference
208
+ - Deploy to Hugging Face Spaces
209
+ - Confirm `/reset` returns 200
210
+
211
+ **Phase 6 — Validation & Hardening (0.5–1 day)**
212
+ - Run pre-submission validation script
213
+ - Add input size checks
214
+ - Add timeouts and safe error handling
215
+ - Pin dependencies for reproducibility
216
+
217
+ **Phase 7 — Documentation & Release (0.5 day)**
218
+ - README with setup, schema, tasks, baseline scores
219
+ - Troubleshooting steps
220
+ - Submission checklist
221
+
222
+ ### Required Files
223
+ - `openenv.yaml`
224
+ - `environment.py` (or equivalent)
225
+ - `inference.py` (root)
226
+ - `Dockerfile`
227
+ - `README.md`
228
+ - `dataset/` or `samples/` (Solidity snippets + labels)
229
+
230
+ ### Success Criteria
231
+ - HF Space deploys and responds to `/reset`
232
+ - Docker build passes
233
+ - `inference.py` completes under 20 minutes
234
+ - Strict log format compliance
235
+ - All graders return normalized scores
236
+
237
+ ---
238
+
239
+ ## Sample Test Cases
240
+
241
+ ### Test Case 1: Reentrancy Vulnerability
242
+ ```solidity
243
+ // SPDX-License-Identifier: MIT
244
+ pragma solidity ^0.8.0;
245
+
246
+ contract Vulnerable {
247
+ mapping(address => uint) balances;
248
+
249
+ function withdraw() public {
250
+ uint bal = balances[msg.sender];
251
+ require(bal > 0);
252
+
253
+ (bool success, ) = msg.sender.call{value: bal}("");
254
+ require(success);
255
+
256
+ balances[msg.sender] = 0;
257
+ }
258
+ }
259
+ ```
260
+ **Expected**: Agent should detect reentrancy vulnerability (Task 3)
261
+
262
+ ### Test Case 2: Missing Access Control
263
+ ```solidity
264
+ // SPDX-License-Identifier: MIT
265
+ pragma solidity ^0.8.0;
266
+
267
+ contract AdminContract {
268
+ mapping(address => bool) admins;
269
+
270
+ function setAdmin(address addr, bool status) public {
271
+ admins[addr] = status;
272
+ }
273
+ }
274
+ ```
275
+ **Expected**: Agent should identify missing access control (Task 3)
276
+
277
+ ---
278
+
279
+ ## References
280
+
281
+ - OpenEnv Framework: https://github.com/raun/openenv-course
282
+ - Common Smart Contract Vulnerabilities: Slither, Mythril
283
+ - Gas Optimization: Solidity docs, OpenZeppelin
284
+
285
+ ---
286
+
287
+ *Last Updated: April 3, 2026*
inference.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import sys
6
+ from typing import Any, Dict, List
7
+
8
+ from environment import SolidityGuardEnv
9
+
10
+
11
+ def _log(tag: str, payload: Dict[str, Any]) -> None:
12
+ print(f"[{tag}] {json.dumps(payload, ensure_ascii=True)}")
13
+
14
+
15
+ def _load_env_var(name: str) -> str:
16
+ value = os.getenv(name)
17
+ if not value:
18
+ raise RuntimeError(f"Missing required environment variable: {name}")
19
+ return value
20
+
21
+
22
+ def _call_model(prompt: str) -> List[Dict[str, Any]]:
23
+ from openai import OpenAI
24
+
25
+ api_base_url = _load_env_var("API_BASE_URL")
26
+ model_name = _load_env_var("MODEL_NAME")
27
+ hf_token = _load_env_var("HF_TOKEN")
28
+
29
+ client = OpenAI(base_url=api_base_url, api_key=hf_token)
30
+ response = client.chat.completions.create(
31
+ model=model_name,
32
+ messages=[
33
+ {"role": "system", "content": "You are a Solidity security reviewer."},
34
+ {"role": "user", "content": prompt},
35
+ ],
36
+ temperature=0.0,
37
+ max_tokens=800,
38
+ )
39
+
40
+ content = response.choices[0].message.content or "[]"
41
+ try:
42
+ parsed = json.loads(content)
43
+ except json.JSONDecodeError:
44
+ parsed = []
45
+ if isinstance(parsed, list):
46
+ return parsed
47
+ return []
48
+
49
+
50
+ def _build_prompt(source_code: str, task_id: str) -> str:
51
+ return (
52
+ "Review the Solidity contract and return a JSON array of findings. "
53
+ "Each finding must include: issue_type, line_number, description, severity. "
54
+ f"Task: {task_id}.\n\n"
55
+ f"Contract:\n{source_code}"
56
+ )
57
+
58
+
59
+ def run() -> int:
60
+ env = SolidityGuardEnv()
61
+ tasks = [
62
+ "task_1_best_practices",
63
+ "task_2_gas_optimization",
64
+ "task_3_security",
65
+ ]
66
+
67
+ _log("START", {"task_count": len(tasks)})
68
+ total_score = 0.0
69
+
70
+ for task_id in tasks:
71
+ observation = env.reset(task_id=task_id)
72
+ prompt = _build_prompt(observation["source_code"], task_id)
73
+ actions = _call_model(prompt)
74
+
75
+ step_result = env.step(actions)
76
+ state = env.state()
77
+
78
+ total_score += step_result["reward"]
79
+
80
+ _log(
81
+ "STEP",
82
+ {
83
+ "task_id": task_id,
84
+ "reward": step_result["reward"],
85
+ "details": step_result.get("details", {}),
86
+ "state": state,
87
+ },
88
+ )
89
+
90
+ final_score = round(total_score / len(tasks), 4)
91
+ _log("END", {"final_score": final_score})
92
+ return 0
93
+
94
+
95
+ if __name__ == "__main__":
96
+ try:
97
+ sys.exit(run())
98
+ except Exception as exc:
99
+ _log("END", {"final_score": 0.0, "error": str(exc)})
100
+ sys.exit(1)
openenv.yaml ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: SolidityGuard
2
+ version: 0.1.0
3
+ description: OpenEnv RL environment for Solidity security review
4
+ entrypoint: environment.py:SolidityGuardEnv
5
+ tasks:
6
+ - id: task_1_best_practices
7
+ difficulty: easy
8
+ description: Detect syntax and best-practice issues in Solidity contracts.
9
+ max_steps: 1
10
+ - id: task_2_gas_optimization
11
+ difficulty: medium
12
+ description: Detect gas optimization opportunities in Solidity contracts.
13
+ max_steps: 1
14
+ - id: task_3_security
15
+ difficulty: hard
16
+ description: Detect security vulnerabilities in Solidity contracts.
17
+ max_steps: 1
18
+ schemas:
19
+ observation:
20
+ type: object
21
+ required: [source_code, metadata, task_id]
22
+ properties:
23
+ source_code:
24
+ type: string
25
+ metadata:
26
+ type: object
27
+ required: [contract_name, compiler_version, file_path]
28
+ properties:
29
+ contract_name:
30
+ type: string
31
+ compiler_version:
32
+ type: string
33
+ file_path:
34
+ type: string
35
+ task_id:
36
+ type: string
37
+ action:
38
+ type: array
39
+ items:
40
+ type: object
41
+ required: [issue_type, line_number, description, severity]
42
+ properties:
43
+ issue_type:
44
+ type: string
45
+ line_number:
46
+ type: integer
47
+ nullable: true
48
+ description:
49
+ type: string
50
+ severity:
51
+ type: string
52
+ enum: [Critical, Medium, Low, Info]
53
+ state:
54
+ type: object
55
+ required: [task_id, step_count, max_steps, score_so_far, done]
56
+ properties:
57
+ task_id:
58
+ type: string
59
+ step_count:
60
+ type: integer
61
+ max_steps:
62
+ type: integer
63
+ score_so_far:
64
+ type: number
65
+ done:
66
+ type: boolean
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ fastapi>=0.110.0
2
+ openai>=1.30.0
3
+ uvicorn>=0.27.0