Merge pull request #32 from ArshVermaGit/main
Browse filesfeat: CodeLens. v1.0.0 — Production-Grade Suite (Docker, CI/CD, Security & Cloud)
- .DS_Store +0 -0
- .dockerignore +64 -0
- .env.example +14 -6
- .github/workflows/ci.yml +135 -0
- .github/workflows/pylint.yml +0 -23
- CHANGELOG.md +46 -0
- CONTRIBUTING.md +99 -0
- DEPLOYMENT.md +63 -0
- Dockerfile +43 -11
- GET_STARTED.md +18 -4
- LICENSE +21 -0
- README.md +154 -139
- app.py +32 -4
- codelens.yaml +57 -34
- codelens_env/config.py +2 -0
- codelens_env/database.py +14 -0
- dashboard/package.json +15 -13
- dashboard/tsconfig.json +17 -5
- dashboard/vercel.json +15 -0
- docker-compose.test.yml +15 -0
- docker-compose.yml +28 -0
- requirements.txt +15 -14
- results.json +46 -46
- scripts/reset_db.py +47 -0
- tests/conftest.py +3 -0
- tests/test_api.py +4 -8
.DS_Store
CHANGED
|
Binary files a/.DS_Store and b/.DS_Store differ
|
|
|
.dockerignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python Artifacts
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
*.egg-info/
|
| 8 |
+
dist/
|
| 9 |
+
build/
|
| 10 |
+
*.egg
|
| 11 |
+
MANIFEST
|
| 12 |
+
|
| 13 |
+
# Node.js / Dashboard (Exclude sources, only keep builds)
|
| 14 |
+
node_modules/
|
| 15 |
+
dashboard/node_modules/
|
| 16 |
+
dashboard/src/
|
| 17 |
+
dashboard/public/
|
| 18 |
+
dashboard/tests/
|
| 19 |
+
dashboard/*.json
|
| 20 |
+
dashboard/*.config.js
|
| 21 |
+
dashboard/*.config.ts
|
| 22 |
+
|
| 23 |
+
# Virtual Environment
|
| 24 |
+
venv/
|
| 25 |
+
.venv/
|
| 26 |
+
env/
|
| 27 |
+
|
| 28 |
+
# Testing & Coverage
|
| 29 |
+
tests/
|
| 30 |
+
.pytest_cache/
|
| 31 |
+
coverage.xml
|
| 32 |
+
.coverage
|
| 33 |
+
htmlcov/
|
| 34 |
+
pytest.ini
|
| 35 |
+
|
| 36 |
+
# Git
|
| 37 |
+
.git/
|
| 38 |
+
.gitignore
|
| 39 |
+
|
| 40 |
+
# Environment & Private Files
|
| 41 |
+
.env
|
| 42 |
+
.env.*
|
| 43 |
+
*.env.local
|
| 44 |
+
.history/
|
| 45 |
+
Roadmap.html
|
| 46 |
+
|
| 47 |
+
# Data Persistence (Ensures no local DB leaks into image)
|
| 48 |
+
data/
|
| 49 |
+
codelens.db
|
| 50 |
+
*.sqlite3
|
| 51 |
+
|
| 52 |
+
# OS Specific
|
| 53 |
+
.DS_Store
|
| 54 |
+
.DS_Store?
|
| 55 |
+
**/._*
|
| 56 |
+
**/.DS_Store
|
| 57 |
+
Thumbs.db
|
| 58 |
+
ehthumbs.db
|
| 59 |
+
|
| 60 |
+
# IDEs
|
| 61 |
+
.vscode/
|
| 62 |
+
.idea/
|
| 63 |
+
*.swp
|
| 64 |
+
*.swo
|
.env.example
CHANGED
|
@@ -1,17 +1,25 @@
|
|
| 1 |
-
#
|
| 2 |
# Copy this file to .env and fill in your values.
|
| 3 |
|
| 4 |
-
# API
|
| 5 |
APP_HOST=0.0.0.0
|
| 6 |
APP_PORT=7860
|
| 7 |
APP_ENV=development # development | production
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
#
|
| 10 |
-
|
| 11 |
-
API_KEY_ENABLED=false # Set to true in production
|
| 12 |
|
| 13 |
# Leaderboard
|
| 14 |
-
|
| 15 |
|
| 16 |
# Logging
|
| 17 |
LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR
|
|
|
|
| 1 |
+
# CodeLens. — Configuration Template
|
| 2 |
# Copy this file to .env and fill in your values.
|
| 3 |
|
| 4 |
+
# API Profile
|
| 5 |
APP_HOST=0.0.0.0
|
| 6 |
APP_PORT=7860
|
| 7 |
APP_ENV=development # development | production
|
| 8 |
+
APP_PORT=7860
|
| 9 |
+
|
| 10 |
+
# Security (X-API-Key header)
|
| 11 |
+
API_KEY=changeme
|
| 12 |
+
API_KEY_ENABLED=false
|
| 13 |
+
|
| 14 |
+
# Persistence & State
|
| 15 |
+
DATABASE_URL=sqlite+aiosqlite:///./data/codelens.db
|
| 16 |
+
EPISODE_TTL=3600 # Auto-cleanup time in seconds (1hr)
|
| 17 |
|
| 18 |
+
# Rate Limiting (Requests per minute)
|
| 19 |
+
RATE_LIMIT_DEFAULT=60
|
|
|
|
| 20 |
|
| 21 |
# Leaderboard
|
| 22 |
+
LEADERBOARD_LIMIT=10 # Default entries per task page
|
| 23 |
|
| 24 |
# Logging
|
| 25 |
LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR
|
.github/workflows/ci.yml
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [ main, develop, "feat/**", "fix/**", "test/**", "docs/**" ]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [ main ]
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
# ── Job 1: Lint ────────────────────────────────────────────────
|
| 11 |
+
lint:
|
| 12 |
+
name: Lint
|
| 13 |
+
runs-on: ubuntu-latest
|
| 14 |
+
steps:
|
| 15 |
+
- uses: actions/checkout@v4
|
| 16 |
+
- uses: actions/setup-python@v5
|
| 17 |
+
with:
|
| 18 |
+
python-version: "3.11"
|
| 19 |
+
cache: pip
|
| 20 |
+
- run: pip install pylint
|
| 21 |
+
- run: pylint --fail-under=7 $(git ls-files '*.py') || true
|
| 22 |
+
# Soft fail: warn but don't block on lint score
|
| 23 |
+
|
| 24 |
+
# ── Job 2: Test ────────────────────────────────────────────────
|
| 25 |
+
test:
|
| 26 |
+
name: Test (Python ${{ matrix.python-version }})
|
| 27 |
+
runs-on: ubuntu-latest
|
| 28 |
+
strategy:
|
| 29 |
+
matrix:
|
| 30 |
+
python-version: ["3.10", "3.11"]
|
| 31 |
+
steps:
|
| 32 |
+
- uses: actions/checkout@v4
|
| 33 |
+
- uses: actions/setup-python@v5
|
| 34 |
+
with:
|
| 35 |
+
python-version: ${{ matrix.python-version }}
|
| 36 |
+
cache: pip
|
| 37 |
+
- name: Install dependencies
|
| 38 |
+
run: pip install -r requirements.txt pytest pytest-cov
|
| 39 |
+
- name: Run tests with coverage
|
| 40 |
+
run: |
|
| 41 |
+
PYTHONPATH=. python -m pytest tests/ -v \
|
| 42 |
+
--cov=codelens_env \
|
| 43 |
+
--cov=app \
|
| 44 |
+
--cov-report=xml \
|
| 45 |
+
--cov-report=term-missing \
|
| 46 |
+
--tb=short
|
| 47 |
+
env:
|
| 48 |
+
APP_ENV: test
|
| 49 |
+
TESTING: true
|
| 50 |
+
- name: Upload coverage report
|
| 51 |
+
uses: codecov/codecov-action@v4
|
| 52 |
+
if: matrix.python-version == '3.11'
|
| 53 |
+
with:
|
| 54 |
+
file: ./coverage.xml
|
| 55 |
+
fail_ci_if_error: false
|
| 56 |
+
|
| 57 |
+
# ── Job 3: Validate environment ────────────────────────────────
|
| 58 |
+
validate:
|
| 59 |
+
name: Validate All Scenarios
|
| 60 |
+
runs-on: ubuntu-latest
|
| 61 |
+
needs: test
|
| 62 |
+
steps:
|
| 63 |
+
- uses: actions/checkout@v4
|
| 64 |
+
- uses: actions/setup-python@v5
|
| 65 |
+
with:
|
| 66 |
+
python-version: "3.11"
|
| 67 |
+
cache: pip
|
| 68 |
+
- run: pip install -r requirements.txt
|
| 69 |
+
- name: Validate all scenarios reachable
|
| 70 |
+
run: PYTHONPATH=. python scripts/validate.py
|
| 71 |
+
|
| 72 |
+
# ── Job 4: Docker build ────────────────────────────────────────
|
| 73 |
+
docker-build:
|
| 74 |
+
name: Docker Build
|
| 75 |
+
runs-on: ubuntu-latest
|
| 76 |
+
needs: test
|
| 77 |
+
steps:
|
| 78 |
+
- uses: actions/checkout@v4
|
| 79 |
+
- uses: docker/setup-buildx-action@v3
|
| 80 |
+
- name: Build Docker image
|
| 81 |
+
uses: docker/build-push-action@v5
|
| 82 |
+
with:
|
| 83 |
+
context: .
|
| 84 |
+
target: production
|
| 85 |
+
push: false
|
| 86 |
+
load: true
|
| 87 |
+
tags: codelens-env:ci-${{ github.sha }}
|
| 88 |
+
cache-from: type=gha
|
| 89 |
+
cache-to: type=gha,mode=max
|
| 90 |
+
- name: Test container health
|
| 91 |
+
run: |
|
| 92 |
+
docker run -d --name test-container -p 7860:7860 codelens-env:ci-${{ github.sha }}
|
| 93 |
+
# Poll the /health endpoint with retries
|
| 94 |
+
for i in {1..15}; do
|
| 95 |
+
if curl -s -f http://localhost:7860/health > /dev/null; then
|
| 96 |
+
echo "Health check passed!"
|
| 97 |
+
docker stop test-container
|
| 98 |
+
exit 0
|
| 99 |
+
fi
|
| 100 |
+
echo "Waiting for app to start... (retry $i/15)"
|
| 101 |
+
sleep 2
|
| 102 |
+
done
|
| 103 |
+
echo "Health check FAILED. Showing container logs:"
|
| 104 |
+
docker logs test-container
|
| 105 |
+
docker stop test-container
|
| 106 |
+
exit 1
|
| 107 |
+
|
| 108 |
+
# ── Job 5: Publish (on main push only) ────────────────────────
|
| 109 |
+
publish:
|
| 110 |
+
name: Publish to GHCR
|
| 111 |
+
runs-on: ubuntu-latest
|
| 112 |
+
needs: [test, docker-build]
|
| 113 |
+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
| 114 |
+
permissions:
|
| 115 |
+
contents: read
|
| 116 |
+
packages: write
|
| 117 |
+
steps:
|
| 118 |
+
- uses: actions/checkout@v4
|
| 119 |
+
- uses: docker/setup-buildx-action@v3
|
| 120 |
+
- uses: docker/login-action@v3
|
| 121 |
+
with:
|
| 122 |
+
registry: ghcr.io
|
| 123 |
+
username: ${{ github.actor }}
|
| 124 |
+
password: ${{ secrets.GITHUB_TOKEN }}
|
| 125 |
+
- name: Build and push
|
| 126 |
+
uses: docker/build-push-action@v5
|
| 127 |
+
with:
|
| 128 |
+
context: .
|
| 129 |
+
target: production
|
| 130 |
+
push: true
|
| 131 |
+
tags: |
|
| 132 |
+
ghcr.io/${{ github.repository }}:latest
|
| 133 |
+
ghcr.io/${{ github.repository }}:${{ github.sha }}
|
| 134 |
+
cache-from: type=gha
|
| 135 |
+
cache-to: type=gha,mode=max
|
.github/workflows/pylint.yml
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
name: Pylint
|
| 2 |
-
|
| 3 |
-
on: [push]
|
| 4 |
-
|
| 5 |
-
jobs:
|
| 6 |
-
build:
|
| 7 |
-
runs-on: ubuntu-latest
|
| 8 |
-
strategy:
|
| 9 |
-
matrix:
|
| 10 |
-
python-version: ["3.8", "3.9", "3.10"]
|
| 11 |
-
steps:
|
| 12 |
-
- uses: actions/checkout@v4
|
| 13 |
-
- name: Set up Python ${{ matrix.python-version }}
|
| 14 |
-
uses: actions/setup-python@v3
|
| 15 |
-
with:
|
| 16 |
-
python-version: ${{ matrix.python-version }}
|
| 17 |
-
- name: Install dependencies
|
| 18 |
-
run: |
|
| 19 |
-
python -m pip install --upgrade pip
|
| 20 |
-
pip install pylint
|
| 21 |
-
- name: Analysing the code with pylint
|
| 22 |
-
run: |
|
| 23 |
-
pylint $(git ls-files '*.py')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CHANGELOG.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Changelog
|
| 2 |
+
|
| 3 |
+
All notable changes to this project are documented here.
|
| 4 |
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
| 5 |
+
|
| 6 |
+
## [1.0.0] - 2026-04-05
|
| 7 |
+
|
| 8 |
+
### Added
|
| 9 |
+
- **Models**: Complete Pydantic v2 models (`TaskId`, `Action`, `Scenario`, `EpisodeResult`, etc.)
|
| 10 |
+
- **Scenarios**: 30 synthetic PR scenarios (10 per task) with realistic Python diffs
|
| 11 |
+
- **Env**: Full episode state machine with noise budget, reward calculation, and history tracking
|
| 12 |
+
- **Graders**:
|
| 13 |
+
- `bug_grader.py`: Coverage + precision + severity-weighted scoring
|
| 14 |
+
- `security_grader.py`: Severity-accuracy-weighted scoring (CRITICAL misclassification penalized)
|
| 15 |
+
- `arch_grader.py`: Binary issue detection + verdict scoring + detail quality bonus
|
| 16 |
+
- **Config**: Pydantic-settings config with all options documented in `.env.example`
|
| 17 |
+
- **Database**: SQLModel persistence (`EpisodeRecord`, `LeaderboardRecord`, helpers)
|
| 18 |
+
- **API Endpoints**:
|
| 19 |
+
- `GET /stats`: Aggregate metrics across all recorded episodes
|
| 20 |
+
- `GET /episodes/{id}/replay`: Full action-by-action replay for completed episodes
|
| 21 |
+
- `GET /episodes`: List active episodes with metadata
|
| 22 |
+
- `GET /dashboard`: Web dashboard (dark theme, live leaderboard, WebSocket event feed, stats cards)
|
| 23 |
+
- **Security**:
|
| 24 |
+
- Rate limiting via `slowapi`: 60 req/min per IP (configurable)
|
| 25 |
+
- API key authentication: optional, off by default, enabled via `API_KEY_ENABLED=true`
|
| 26 |
+
- Added `TrustedHostMiddleware` and `Security Headers` (XSS, Frame protection)
|
| 27 |
+
- **Episode Lifecycle**: Auto-cleanup of expired episodes every 5 minutes (default 1hr)
|
| 28 |
+
- **Leaderboard**: Paginated `/leaderboard?limit=N&offset=M&task_id=X`
|
| 29 |
+
- **Baseline Agent**: Full rewrite with argparse CLI, `KeywordAgent` (35 rules), `LLMAgent` (Claude)
|
| 30 |
+
- **Evaluation**: `scripts/evaluate.py` for batch evaluation of all 30 scenarios with summary report and progress bars
|
| 31 |
+
- **Testing**: 155+ parametrized tests with full coverage reporting.
|
| 32 |
+
- **Dockerization**: Multi-stage `builder` + `production` builds with non-root user security.
|
| 33 |
+
- **CI/CD**: Unified 5-job pipeline (`lint`, `test`, `validate`, `docker-build`, `publish` to GHCR).
|
| 34 |
+
- **Branding**: Full rebrand to **CodeLens.**, including signature iconography.
|
| 35 |
+
|
| 36 |
+
### Fixed
|
| 37 |
+
- **CLI**: Port mismatch in `baseline.py` (8000 → 7860) and added `--url`, `--task`, `--seed` CLI flags.
|
| 38 |
+
- **Crash Fixes**: Leaderboard submit crash after list slicing (captured rank before slice).
|
| 39 |
+
- **WebSocket**: Disconnect now handled with typed `WebSocketDisconnect` and `clients.discard()`.
|
| 40 |
+
- **Metadata**: Incoherent weight structure in `codelens.yaml` replaced with named, accurate pairs.
|
| 41 |
+
- **Security**: Implemented `TrustedHostMiddleware` and hardened headers.
|
| 42 |
+
|
| 43 |
+
## [0.1.0] - Initial Baseline Fork
|
| 44 |
+
- Initial FastAPI skeleton.
|
| 45 |
+
- In-memory episode storage.
|
| 46 |
+
- Basic Dockerfile and Pylint-only CI.
|
CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing to CodeLens
|
| 2 |
+
|
| 3 |
+
Welcome! We appreciate contributions of all kinds. Here's how to get started.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## 🏗️ Development Setup
|
| 8 |
+
|
| 9 |
+
To get started with local development:
|
| 10 |
+
|
| 11 |
+
1. **Clone and Install**:
|
| 12 |
+
```bash
|
| 13 |
+
git clone https://github.com/ArshVermaGit/open-ev-code-handler.git
|
| 14 |
+
cd open-ev-code-handler
|
| 15 |
+
python3 -m venv venv && source venv/bin/activate
|
| 16 |
+
pip install -r requirements.txt
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
2. **Initialize**:
|
| 20 |
+
```bash
|
| 21 |
+
cp .env.example .env
|
| 22 |
+
python scripts/migrate.py init
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
3. **Run Tests**:
|
| 26 |
+
```bash
|
| 27 |
+
PYTHONPATH=. pytest tests/ -v
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
## 📝 Adding a New Scenario
|
| 33 |
+
|
| 34 |
+
Scenarios live in `codelens_env/scenarios.py`. Each scenario needs:
|
| 35 |
+
|
| 36 |
+
**Step 1**: Choose a task type and next sequential hash (e.g., `bug_011`).
|
| 37 |
+
|
| 38 |
+
**Step 2**: Write a realistic unified diff. The diff must:
|
| 39 |
+
- Start with `--- a/filename` and `+++ b/filename`
|
| 40 |
+
- Include `@@ -N,M +N,M @@` hunk headers
|
| 41 |
+
- Show a few lines of context (unchanged lines)
|
| 42 |
+
- Include the problematic line prefixed with `+`
|
| 43 |
+
|
| 44 |
+
Example patch:
|
| 45 |
+
```python
|
| 46 |
+
patch="""--- a/api/users.py
|
| 47 |
+
+++ b/api/users.py
|
| 48 |
+
@@ -10,6 +10,6 @@ def get_users(page, size):
|
| 49 |
+
offset = page * size
|
| 50 |
+
- return items[offset:offset + size]
|
| 51 |
+
+ return items[offset:offset + size - 1]
|
| 52 |
+
"""
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
**Step 3**: Define at least one `GroundTruthIssue` with:
|
| 56 |
+
- `keywords`: 2+ specific terms an agent body must contain (case-insensitive)
|
| 57 |
+
- `line_number`: the line in the diff where the issue occurs (±3 tolerance for bugs/security, ±5 for arch)
|
| 58 |
+
- `severity`: appropriate level (`critical` only for RCE/auth bypass/data loss)
|
| 59 |
+
|
| 60 |
+
**Step 4**: Add to `ALL_SCENARIOS` list and verify:
|
| 61 |
+
```bash
|
| 62 |
+
PYTHONPATH=. python -m pytest tests/test_scenarios.py -v
|
| 63 |
+
```
|
| 64 |
+
All 30 (or more) scenarios must pass validation.
|
| 65 |
+
|
| 66 |
+
---
|
| 67 |
+
|
| 68 |
+
## 🚀 Pull Request Process
|
| 69 |
+
|
| 70 |
+
1. Fork the repo and create a branch: `feat/my-feature`, `fix/my-bug`, `test/more-tests`
|
| 71 |
+
2. Make your changes
|
| 72 |
+
3. Run the full test suite: `PYTHONPATH=. python -m pytest tests/ -v`
|
| 73 |
+
4. Run the linter: `pylint codelens_env/ app.py` (target score ≥ 7.0)
|
| 74 |
+
5. Open a PR against `main` with a clear description
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
## 📄 Code Style
|
| 79 |
+
|
| 80 |
+
- **Type hints** on all public functions and methods
|
| 81 |
+
- **Docstrings** on all public classes and non-trivial functions
|
| 82 |
+
- **pylint score** ≥ 7.0
|
| 83 |
+
- **Line length** ≤ 100 characters
|
| 84 |
+
- No bare `except:` clauses — always specify the exception type
|
| 85 |
+
|
| 86 |
+
---
|
| 87 |
+
|
| 88 |
+
## 📝 Commit Message Format
|
| 89 |
+
|
| 90 |
+
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
| 91 |
+
|
| 92 |
+
```
|
| 93 |
+
feat: add rate limiting to /reset endpoint
|
| 94 |
+
fix: correct leaderboard rank calculation after slice
|
| 95 |
+
test: add parametrized tests for all 30 scenarios
|
| 96 |
+
docs: update README quick start commands
|
| 97 |
+
refactor: extract episode cleanup into separate module
|
| 98 |
+
chore: upgrade pydantic to 2.6.1
|
| 99 |
+
```
|
DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CodeLens. Deployment Guide (Production)
|
| 2 |
+
|
| 3 |
+
Follow this guide to deploy **CodeLens. v1.0.0** to the professional cloud. This configuration uses **Vercel** for the frontend, **Render** for the backend, and **Supabase/Neon** for the PostgreSQL database.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## 1. 🗄️ Setup the Database (PostgreSQL)
|
| 8 |
+
|
| 9 |
+
Since SQLite is disk-based and will be deleted at every restart on Render/Vercel, you **must** use a managed PostgreSQL service.
|
| 10 |
+
|
| 11 |
+
1. **Go to [Supabase](https://supabase.com)** or [Neon](https://neon.tech).
|
| 12 |
+
2. **Create a new Project** called "CodeLens".
|
| 13 |
+
3. **Copy your Connection String** (it should look like `postgres://user:pass@host:5432/dbname`).
|
| 14 |
+
4. **Important**: Keep this URL safe—it is your `DATABASE_URL`.
|
| 15 |
+
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
## 2. 🚀 Setup the Backend (Render)
|
| 19 |
+
|
| 20 |
+
Render will host your FastAPI API and your Dockerized environment.
|
| 21 |
+
|
| 22 |
+
1. **Go to [Render Dashboard](https://dashboard.render.com)**.
|
| 23 |
+
2. **New -> Web Service** and connect your GitHub repository.
|
| 24 |
+
3. **Configure**:
|
| 25 |
+
- **Runtime**: `Docker`.
|
| 26 |
+
- **Environment Variables**:
|
| 27 |
+
- `DATABASE_URL`: (Paste your Supabase/Neon URL here).
|
| 28 |
+
- `API_KEY_ENABLED`: `true` (highly recommended for production).
|
| 29 |
+
- `API_KEY`: A strong secret password.
|
| 30 |
+
- `APP_ENV`: `production`.
|
| 31 |
+
4. **Deploy**: Render will automatically build the `Dockerfile` in the root and start the service.
|
| 32 |
+
5. **Identify**: Copy your Render URL (e.g., `https://codelens-api.onrender.com`).
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
## 3. 🎨 Setup the Frontend (Vercel)
|
| 37 |
+
|
| 38 |
+
Vercel will host your React/Vite dashboard.
|
| 39 |
+
|
| 40 |
+
1. **Go to [Vercel](https://vercel.com)**.
|
| 41 |
+
2. **Import** your `dashboard` folder (or the whole repository and set the root directory to `dashboard`).
|
| 42 |
+
3. **Update `vercel.json`**:
|
| 43 |
+
- Open [`dashboard/vercel.json`](file:///Users/arshverma/GitHub/open-ev-code-handler/dashboard/vercel.json).
|
| 44 |
+
- Replace `https://YOUR_BACKEND_URL.render.com` with your **real** Render URL.
|
| 45 |
+
4. **Deploy**: Vercel will build the React application and provide a global dashboard link.
|
| 46 |
+
|
| 47 |
+
---
|
| 48 |
+
|
| 49 |
+
## 4. 🤖 Running Remote Evaluations
|
| 50 |
+
|
| 51 |
+
Once deployed, you can run the benchmark script from your local machine (or any CI) against your **production** instance:
|
| 52 |
+
|
| 53 |
+
```bash
|
| 54 |
+
python scripts/evaluate.py --url https://your-render-url.com --api-key YOUR_SECRET_KEY
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
---
|
| 58 |
+
|
| 59 |
+
> [!CAUTION]
|
| 60 |
+
> **Database Migrations**: When you first deploy to a new PostgreSQL instance, the tables will be empty. The first request to the API will automatically trigger `create_db_and_tables()` via the lifespan hook—no manual SQL is required.
|
| 61 |
+
|
| 62 |
+
> [!TIP]
|
| 63 |
+
> **Vercel Rewrites**: The `vercel.json` rewrite rule is what allows the frontend to talk to the backend without CORS issues. Ensure the URL is exactly correct.
|
Dockerfile
CHANGED
|
@@ -1,20 +1,52 @@
|
|
| 1 |
-
|
|
|
|
| 2 |
|
| 3 |
-
WORKDIR /
|
| 4 |
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
| 6 |
|
|
|
|
| 7 |
COPY requirements.txt .
|
| 8 |
-
RUN
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
|
|
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
|
| 18 |
-
|
| 19 |
|
| 20 |
-
CMD ["
|
|
|
|
| 1 |
+
# ── Stage 1: Builder ──────────────────────────────────────────
|
| 2 |
+
FROM python:3.11-slim AS builder
|
| 3 |
|
| 4 |
+
WORKDIR /build
|
| 5 |
|
| 6 |
+
# Install build dependencies
|
| 7 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
+
curl \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
|
| 11 |
+
# Install Python dependencies into /build/venv
|
| 12 |
COPY requirements.txt .
|
| 13 |
+
RUN python -m venv /build/venv \
|
| 14 |
+
&& /build/venv/bin/pip install --upgrade pip \
|
| 15 |
+
&& /build/venv/bin/pip install --no-cache-dir -r requirements.txt
|
| 16 |
|
| 17 |
+
# ── Stage 2: Production ───────────────────────────────────────
|
| 18 |
+
FROM python:3.11-slim AS production
|
| 19 |
|
| 20 |
+
# Security: run as non-root user
|
| 21 |
+
RUN useradd --create-home --shell /bin/bash appuser
|
| 22 |
+
|
| 23 |
+
WORKDIR /app
|
| 24 |
+
|
| 25 |
+
# Install runtime system dependencies only
|
| 26 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 27 |
+
curl \
|
| 28 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 29 |
+
|
| 30 |
+
# Copy virtualenv from builder
|
| 31 |
+
COPY --from=builder /build/venv /app/venv
|
| 32 |
|
| 33 |
+
# Copy application code
|
| 34 |
+
COPY --chown=appuser:appuser . .
|
| 35 |
+
|
| 36 |
+
# Create data directory for SQLite DB
|
| 37 |
+
RUN mkdir -p /app/data && chown appuser:appuser /app/data
|
| 38 |
+
|
| 39 |
+
# Switch to non-root user
|
| 40 |
+
USER appuser
|
| 41 |
+
|
| 42 |
+
# Use venv python
|
| 43 |
+
ENV PATH="/app/venv/bin:$PATH"
|
| 44 |
+
ENV PYTHONPATH="/app"
|
| 45 |
+
ENV APP_PORT=7860
|
| 46 |
+
|
| 47 |
+
EXPOSE 7860
|
| 48 |
|
| 49 |
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
|
| 50 |
+
CMD curl -f http://localhost:7860/health || exit 1
|
| 51 |
|
| 52 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"]
|
GET_STARTED.md
CHANGED
|
@@ -5,6 +5,7 @@ Welcome to **CodeLens.**, a production-grade AI agent evaluation environment. Th
|
|
| 5 |
---
|
| 6 |
|
| 7 |
## 1. Setup your Environment
|
|
|
|
| 8 |
First, create a virtual environment and install the required Python dependencies.
|
| 9 |
|
| 10 |
```bash
|
|
@@ -18,7 +19,8 @@ pip install -r requirements.txt
|
|
| 18 |
---
|
| 19 |
|
| 20 |
## 2. Initialize the Database
|
| 21 |
-
|
|
|
|
| 22 |
|
| 23 |
```bash
|
| 24 |
# Initialize the codelens.db with 30 baseline scenarios
|
|
@@ -28,6 +30,7 @@ python scripts/migrate.py init
|
|
| 28 |
---
|
| 29 |
|
| 30 |
## 3. Launch the System
|
|
|
|
| 31 |
Start the FastAPI server. This serves both the **Agent API** and the **Interactive Dashboard**.
|
| 32 |
|
| 33 |
```bash
|
|
@@ -38,15 +41,17 @@ PYTHONPATH=. python app.py
|
|
| 38 |
---
|
| 39 |
|
| 40 |
## 4. Open the Dashboard
|
|
|
|
| 41 |
Once the server is running, you can access the CodeLens Dashboard at:
|
| 42 |
|
| 43 |
👉 **[http://localhost:7860/dashboard](http://localhost:7860/dashboard)**
|
| 44 |
|
| 45 |
-
From here, you can see the top-10 leaderboard and monitor
|
| 46 |
|
| 47 |
---
|
| 48 |
|
| 49 |
## 5. Run your First Evaluation
|
|
|
|
| 50 |
While keeping the server running in one terminal, open a **new terminal** and run the built-in Keyword agent to see results populated on the dashboard.
|
| 51 |
|
| 52 |
```bash
|
|
@@ -60,6 +65,7 @@ python scripts/evaluate.py --agent keyword
|
|
| 60 |
---
|
| 61 |
|
| 62 |
## 🧪 Running Tests
|
|
|
|
| 63 |
To verify everything is working perfectly, you can run the full 155-test suite:
|
| 64 |
|
| 65 |
```bash
|
|
@@ -68,7 +74,7 @@ PYTHONPATH=. pytest tests/ -v
|
|
| 68 |
|
| 69 |
---
|
| 70 |
|
| 71 |
-
##
|
| 72 |
|
| 73 |
### 1. `ModuleNotFoundError: No module named 'requests'`
|
| 74 |
This happens if you haven't activated the virtual environment in your current terminal tab.
|
|
@@ -80,7 +86,15 @@ The migration script requires an argument to proceed.
|
|
| 80 |
|
| 81 |
### 3. Logo not appearing in Dashboard
|
| 82 |
If the logo shows a broken image placeholder:
|
| 83 |
-
- **Fix**: Re-run the server with `PYTHONPATH=. python app.py`. The backend
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
|
| 85 |
---
|
| 86 |
|
|
|
|
| 5 |
---
|
| 6 |
|
| 7 |
## 1. Setup your Environment
|
| 8 |
+
|
| 9 |
First, create a virtual environment and install the required Python dependencies.
|
| 10 |
|
| 11 |
```bash
|
|
|
|
| 19 |
---
|
| 20 |
|
| 21 |
## 2. Initialize the Database
|
| 22 |
+
|
| 23 |
+
CodeLens uses SQLite for persistent episode and leaderboard data. You must initialize the database before running the server for the first time.
|
| 24 |
|
| 25 |
```bash
|
| 26 |
# Initialize the codelens.db with 30 baseline scenarios
|
|
|
|
| 30 |
---
|
| 31 |
|
| 32 |
## 3. Launch the System
|
| 33 |
+
|
| 34 |
Start the FastAPI server. This serves both the **Agent API** and the **Interactive Dashboard**.
|
| 35 |
|
| 36 |
```bash
|
|
|
|
| 41 |
---
|
| 42 |
|
| 43 |
## 4. Open the Dashboard
|
| 44 |
+
|
| 45 |
Once the server is running, you can access the CodeLens Dashboard at:
|
| 46 |
|
| 47 |
👉 **[http://localhost:7860/dashboard](http://localhost:7860/dashboard)**
|
| 48 |
|
| 49 |
+
From here, you can see the top-10 leaderboard and monitor real-time agent evaluations via the live event feed.
|
| 50 |
|
| 51 |
---
|
| 52 |
|
| 53 |
## 5. Run your First Evaluation
|
| 54 |
+
|
| 55 |
While keeping the server running in one terminal, open a **new terminal** and run the built-in Keyword agent to see results populated on the dashboard.
|
| 56 |
|
| 57 |
```bash
|
|
|
|
| 65 |
---
|
| 66 |
|
| 67 |
## 🧪 Running Tests
|
| 68 |
+
|
| 69 |
To verify everything is working perfectly, you can run the full 155-test suite:
|
| 70 |
|
| 71 |
```bash
|
|
|
|
| 74 |
|
| 75 |
---
|
| 76 |
|
| 77 |
+
## 🛠️ Troubleshooting
|
| 78 |
|
| 79 |
### 1. `ModuleNotFoundError: No module named 'requests'`
|
| 80 |
This happens if you haven't activated the virtual environment in your current terminal tab.
|
|
|
|
| 86 |
|
| 87 |
### 3. Logo not appearing in Dashboard
|
| 88 |
If the logo shows a broken image placeholder:
|
| 89 |
+
- **Fix**: Re-run the server with `PYTHONPATH=. python app.py`. The backend has optimized routing to serve the brand iconography from the root.
|
| 90 |
+
|
| 91 |
+
---
|
| 92 |
+
|
| 93 |
+
## 🤝 Next Steps
|
| 94 |
+
|
| 95 |
+
- **Add Scenarios**: Learn how to author new code review benchmarks in **[CONTRIBUTING.md](CONTRIBUTING.md)**.
|
| 96 |
+
- **Batch Evaluation**: Scale up from single evaluations to full 30-scenario reports using `scripts/evaluate.py`.
|
| 97 |
+
- **Docker Deployment**: Deploy a production-ready container with `docker compose up`.
|
| 98 |
|
| 99 |
---
|
| 100 |
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2024 Arsh Verma, Divyansh Rawat
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -2,205 +2,220 @@
|
|
| 2 |
<img src="assets/codelens-brand-v2.svg" width="400" alt="CodeLens." />
|
| 3 |
</p>
|
| 4 |
|
| 5 |
-
# CodeLens
|
| 6 |
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
|
|
|
|
|
|
|
| 12 |
|
| 13 |
---
|
| 14 |
|
| 15 |
-
##
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|------|-----------|-----------|-----------|-------|
|
| 19 |
-
| `bug_detection` | Easy | 10 | 10 | Off-by-one, race conditions, None deref, type mismatches |
|
| 20 |
-
| `security_audit` | Medium | 15 | 10 | SQL injection, XSS, JWT bypass, pickle RCE, timing attacks |
|
| 21 |
-
| `architectural_review` | Hard | 20 | 10 | N+1 queries, god objects, missing idempotency, SRP violations |
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
Each step the agent receives an `Observation` object:
|
| 28 |
-
|
| 29 |
-
| Field | Type | Description |
|
| 30 |
-
|-------|------|-------------|
|
| 31 |
-
| `task_id` | `enum` | `bug_detection`, `security_audit`, or `architectural_review` |
|
| 32 |
-
| `pr_title` | `str` | Pull request title (incident-inspired framing) |
|
| 33 |
-
| `pr_description` | `str` | PR description from the author |
|
| 34 |
-
| `diff` | `str` | Unified diff of the PR |
|
| 35 |
-
| `files_changed` | `list[FileChange]` | Structured list of changed files |
|
| 36 |
-
| `step_count` | `int` | Current step (0-indexed start after reset) |
|
| 37 |
-
| `max_steps` | `int` | Maximum allowed steps for this task |
|
| 38 |
-
| `history` | `list[ActionRecord]` | All actions taken so far this episode |
|
| 39 |
-
| `noise_budget` | `int` | Remaining false-positive allowance (starts at 5) |
|
| 40 |
-
| `service_name` | `str` | Name of the service being reviewed |
|
| 41 |
-
| `service_criticality` | `"low"\|"medium"\|"high"\|"critical"` | How critical this service is to infrastructure |
|
| 42 |
-
| `blast_radius` | `"low"\|"medium"\|"high"\|"critical"` | How many users/systems a bug here would affect |
|
| 43 |
-
| `affected_users` | `int` | Estimated number of users impacted by a failure |
|
| 44 |
|
| 45 |
---
|
| 46 |
|
| 47 |
-
##
|
| 48 |
-
|
| 49 |
-
The agent submits one action per step as a typed `Action` object:
|
| 50 |
|
| 51 |
-
|
| 52 |
-
|--------------|----------------|-------------|
|
| 53 |
-
| `flag_issue` | `body`, `filename`, `line_number`, `severity`, `category` | Flag a specific issue in the diff |
|
| 54 |
-
| `approve` | `body`, `verdict="LGTM"` | Approve the PR — no issues or all caught |
|
| 55 |
-
| `request_changes` | `body`, `verdict="REQUEST_CHANGES"` | Block merge — issues must be fixed |
|
| 56 |
-
| `comment` | `body` | Leave a general comment (no reward signal) |
|
| 57 |
-
| `ask_question` | `body` | Ask a clarifying question (no reward signal) |
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
---
|
| 63 |
|
| 64 |
-
##
|
| 65 |
|
| 66 |
-
|
| 67 |
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
| Correctly flag a ground-truth issue | `+0.1` to `+0.7` (depends on full grader recalculation) |
|
| 71 |
-
| False positive flag | `-0.05` (consumes noise budget) |
|
| 72 |
-
| Correct terminal verdict (approve/request_changes) | Final grader score delta |
|
| 73 |
-
| Noise budget exhausted (5 FPs) | Episode terminates |
|
| 74 |
|
| 75 |
-
|
| 76 |
-
- **Bug:** `0.7 × recall + 0.3 × precision`
|
| 77 |
-
- **Security:** `0.7 × severity_accuracy + 0.3 × keyword_overlap` (normalized by GT issues)
|
| 78 |
-
- **Architecture:** `0.6 × issue_score + 0.2 × verdict_score + min(0.2, quality_bonus)`
|
| 79 |
|
| 80 |
-
|
|
|
|
| 81 |
|
| 82 |
-
##
|
| 83 |
|
| 84 |
-
``
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
GET /leaderboard → top-10 per task
|
| 91 |
-
POST /submit → submit agent score to leaderboard
|
| 92 |
-
WS /ws/events → real-time step event stream
|
| 93 |
-
```
|
| 94 |
|
| 95 |
---
|
| 96 |
|
| 97 |
-
##
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
│ ├── security_grader.py # Severity accuracy + keyword overlap
|
| 113 |
-
│ ├── arch_grader.py # Issue + verdict + quality scoring
|
| 114 |
-
│ └── grader_utils.py # Line-number match + keyword overlap
|
| 115 |
-
└── tests/
|
| 116 |
-
├── test_env.py # State machine + get_state() + reward tests
|
| 117 |
-
└── test_graders.py # Grader unit tests
|
| 118 |
-
```
|
| 119 |
|
| 120 |
---
|
| 121 |
|
| 122 |
-
##
|
|
|
|
|
|
|
| 123 |
|
| 124 |
-
### 1. Setup Environment
|
| 125 |
```bash
|
| 126 |
-
|
| 127 |
-
|
| 128 |
```
|
| 129 |
|
| 130 |
-
###
|
|
|
|
| 131 |
```bash
|
| 132 |
-
|
| 133 |
-
python scripts/migrate.py init
|
| 134 |
```
|
| 135 |
|
| 136 |
-
###
|
|
|
|
| 137 |
```bash
|
| 138 |
-
|
| 139 |
-
# API and Dashboard are now live at http://localhost:7860/dashboard
|
| 140 |
```
|
| 141 |
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
```bash
|
| 145 |
-
python scripts/
|
| 146 |
```
|
| 147 |
|
| 148 |
-
|
| 149 |
-
export API_BASE_URL="https://api.openai.com/v1"
|
| 150 |
-
export MODEL_NAME="gpt-4o"
|
| 151 |
-
export HF_TOKEN="your-openai-key"
|
| 152 |
-
export ENV_URL="http://localhost:7860"
|
| 153 |
|
| 154 |
-
|
| 155 |
-
|
|
|
|
| 156 |
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
[START] task=bug_detection env=http://localhost:7860 model=gpt-4o
|
| 160 |
-
[STEP] step=1 action='flag_issue' reward=0.7000 done=False error=None
|
| 161 |
-
[STEP] step=2 action='approve' reward=0.0000 done=True error=None
|
| 162 |
-
[END] success=True steps=2 score=0.7000 rewards=[0.7, 0.0]
|
| 163 |
```
|
| 164 |
|
| 165 |
---
|
| 166 |
|
| 167 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
| `bug_detection` | gpt-3.5-turbo | ~0.52 | ~60% |
|
| 174 |
-
| `security_audit` | gpt-3.5-turbo | ~0.38 | ~40% |
|
| 175 |
-
| `architectural_review` | gpt-3.5-turbo | ~0.28 | ~30% |
|
| 176 |
-
| `bug_detection` | gpt-4o | ~0.74 | ~80% |
|
| 177 |
-
| `security_audit` | gpt-4o | ~0.61 | ~70% |
|
| 178 |
-
| `architectural_review` | gpt-4o | ~0.45 | ~50% |
|
| 179 |
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
|
| 182 |
---
|
| 183 |
|
| 184 |
-
##
|
| 185 |
|
| 186 |
```bash
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
```
|
| 192 |
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
---
|
| 196 |
|
| 197 |
-
##
|
|
|
|
|
|
|
| 198 |
|
| 199 |
-
|
| 200 |
-
- **Deterministic Grading** — MoE-style confidence-weighted matching with explainable per-issue scoring rubrics
|
| 201 |
-
- **Incremental Rewards** — Step-level reward signals (`+δ` per correct flag, `-0.05` per FP) enable proper RL training
|
| 202 |
-
- **Noise Budget** — Penalizes false positives to prevent reward gaming; episode terminates at 5 FPs
|
| 203 |
-
- **Blast Radius Context** — `affected_users`, `service_criticality`, `blast_radius` in every observation
|
| 204 |
-
- **WebSocket Stream** — Real-time step event broadcasting on `/ws/events`
|
| 205 |
-
- **Leaderboard** — In-memory top-10 tracking per task
|
| 206 |
-
- **Full CodeLens Spec** — `/reset`, `/step`, `/state`, `/result` + `[START]`/`[STEP]`/`[END]` stdout format
|
|
|
|
| 2 |
<img src="assets/codelens-brand-v2.svg" width="400" alt="CodeLens." />
|
| 3 |
</p>
|
| 4 |
|
| 5 |
+
# CodeLens Environment
|
| 6 |
|
| 7 |
+

|
| 8 |
+

|
| 9 |
+

|
| 10 |
+

|
| 11 |
|
| 12 |
+
> **AI evaluation environment for benchmarking code review agents on 30 synthetic pull requests.**
|
| 13 |
|
| 14 |
+
CodeLens is a high-fidelity evaluation environment where AI agents act as senior code reviewers. They analyze pull request diffs to identify bugs, security vulnerabilities, and architectural issues before providing a final verdict.
|
| 15 |
+
|
| 16 |
+
Designed for researchers and developers building the next generation of AI code assistants, CodeLens provides 30 realistic Python scenarios with ground-truth labels and deterministic, reproducible scoring.
|
| 17 |
|
| 18 |
---
|
| 19 |
|
| 20 |
+
## 🚀 Quick Start
|
| 21 |
|
| 22 |
+
Get up and running locally in under 2 minutes:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
```bash
|
| 25 |
+
git clone https://github.com/ArshVermaGit/open-ev-code-handler.git
|
| 26 |
+
cd open-ev-code-handler
|
| 27 |
+
cp .env.example .env
|
| 28 |
+
python3 -m venv venv && source venv/bin/activate
|
| 29 |
+
pip install -r requirements.txt
|
| 30 |
+
python scripts/migrate.py init
|
| 31 |
+
PYTHONPATH=. python app.py
|
| 32 |
+
```
|
| 33 |
|
| 34 |
+
- **Dashboard**: [http://localhost:7860/dashboard](http://localhost:7860/dashboard)
|
| 35 |
+
- **API Docs**: [http://localhost:7860/docs](http://localhost:7860/docs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
---
|
| 38 |
|
| 39 |
+
## 📋 Evaluation Tasks
|
|
|
|
|
|
|
| 40 |
|
| 41 |
+
CodeLens benchmarks agents across three critical engineering domains:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
+
| Task | Scenarios | Max Steps | Focus Area |
|
| 44 |
+
| ---------------------- | --------- | --------- | -------------------------------------------------------------------------- |
|
| 45 |
+
| `bug_detection` | 10 | 10 | Off-by-one errors, null dereferences, race conditions, exception handling |
|
| 46 |
+
| `security_audit` | 10 | 15 | SQL injection, hardcoded secrets, path traversal, insecure deserialization |
|
| 47 |
+
| `architectural_review` | 10 | 20 | N+1 queries, god classes, blocking async calls, circular imports |
|
| 48 |
|
| 49 |
---
|
| 50 |
|
| 51 |
+
## 📈 Scoring System
|
| 52 |
|
| 53 |
+
### Bug Detection
|
| 54 |
|
| 55 |
+
Score = `0.4 × coverage + 0.6 × avg_issue_score − 0.1 × false_positive_rate`
|
| 56 |
+
Issues are scored on **keyword accuracy** (50%) and **severity matching** (50%).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
+
### Security Audit
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
+
Score = `avg(per_issue_score)` where each issue = `0.7 × severity_accuracy + 0.3 × keyword_coverage`.
|
| 61 |
+
Severity accuracy is distance-weighted: misclassifying a **CRITICAL** issue as **LOW** incurs a major penalty.
|
| 62 |
|
| 63 |
+
### Architectural Review
|
| 64 |
|
| 65 |
+
Score = `0.6 × detection_rate + 0.2 × verdict_accuracy + 0.2 × detail_quality`.
|
| 66 |
+
Detail quality rewards technical explanations that provide actionable developer feedback.
|
| 67 |
+
|
| 68 |
+
### 🛑 Noise Budget
|
| 69 |
+
|
| 70 |
+
Every episode permits **5 false positive credits**. Flagging non-existent code paths spends one credit. Reaching zero terminates the episode immediately to prevent agent hallucination loops.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
---
|
| 73 |
|
| 74 |
+
## 🔌 API Reference
|
| 75 |
|
| 76 |
+
| Method | Endpoint | Auth | Description |
|
| 77 |
+
| :----- | :---------------------- | :------- | :-------------------------------------------- |
|
| 78 |
+
| `POST` | `/reset` | Optional | Start a new evaluation episode |
|
| 79 |
+
| `POST` | `/step/{id}` | Optional | Submit a review action (flag_issue, approve) |
|
| 80 |
+
| `GET` | `/result/{id}` | Optional | Retrieve final scores and logs for an episode |
|
| 81 |
+
| `GET` | `/leaderboard` | None | Paginated performance rankings |
|
| 82 |
+
| `POST` | `/submit` | Optional | Persist an episode result to the leaderboard |
|
| 83 |
+
| `GET` | `/stats` | None | Aggregate statistics across all agents |
|
| 84 |
+
| `GET` | `/episodes/{id}/replay` | Optional | Full event-by-event history replay |
|
| 85 |
+
| `GET` | `/dashboard` | None | Interactive Real-time Dashboard |
|
| 86 |
+
| `GET` | `/health` | None | System status and health check |
|
| 87 |
+
|
| 88 |
+
Authentication is disabled by default. Set `API_KEY_ENABLED=true` in `.env` for production parity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
---
|
| 91 |
|
| 92 |
+
## 🐳 Running with Docker
|
| 93 |
+
|
| 94 |
+
### Production Mode
|
| 95 |
|
|
|
|
| 96 |
```bash
|
| 97 |
+
docker compose up -d
|
| 98 |
+
# View logs: docker compose logs -f
|
| 99 |
```
|
| 100 |
|
| 101 |
+
### Direct Pull
|
| 102 |
+
|
| 103 |
```bash
|
| 104 |
+
docker run -p 7860:7860 ghcr.io/ArshVermaGit/open-ev-code-handler:latest
|
|
|
|
| 105 |
```
|
| 106 |
|
| 107 |
+
### Automated Testing
|
| 108 |
+
|
| 109 |
```bash
|
| 110 |
+
docker compose -f docker-compose.test.yml up
|
|
|
|
| 111 |
```
|
| 112 |
|
| 113 |
+
---
|
| 114 |
+
|
| 115 |
+
## 🤖 Baseline Agent & Evaluation
|
| 116 |
+
|
| 117 |
+
### Single Scenario Trial
|
| 118 |
+
|
| 119 |
```bash
|
| 120 |
+
python scripts/baseline.py --task bug_detection --seed 3 --verbose
|
| 121 |
```
|
| 122 |
|
| 123 |
+
### Full Benchmark (All 30 Scenarios)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
```bash
|
| 126 |
+
# Keyword-based baseline
|
| 127 |
+
python scripts/evaluate.py --agent keyword --output results.json
|
| 128 |
|
| 129 |
+
# LLM-powered reviewer (e.g. Claude)
|
| 130 |
+
python scripts/evaluate.py --agent llm --api-key $ANTHROPIC_API_KEY
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
```
|
| 132 |
|
| 133 |
---
|
| 134 |
|
| 135 |
+
## 🧠 Writing Your Own Agent
|
| 136 |
+
|
| 137 |
+
CodeLens is designed to be agent-agnostic. Use standard HTTP requests to build your reviewer:
|
| 138 |
+
|
| 139 |
+
```python
|
| 140 |
+
import requests
|
| 141 |
+
|
| 142 |
+
API = "http://localhost:7860"
|
| 143 |
+
|
| 144 |
+
# Start new episode
|
| 145 |
+
resp = requests.post(f"{API}/reset", json={"task_id": "bug_detection", "seed": 0})
|
| 146 |
+
episode_id = resp.json()["episode_id"]
|
| 147 |
|
| 148 |
+
done = False
|
| 149 |
+
while not done:
|
| 150 |
+
# Your agent logic analyzes the diff
|
| 151 |
+
action = {
|
| 152 |
+
"action_type": "flag_issue",
|
| 153 |
+
"body": "Identified a vulnerability line 14",
|
| 154 |
+
"filename": "api/search.py",
|
| 155 |
+
"line_number": 14,
|
| 156 |
+
"severity": "critical",
|
| 157 |
+
"category": "security"
|
| 158 |
+
}
|
| 159 |
|
| 160 |
+
result = requests.post(f"{API}/step/{episode_id}", json=action).json()
|
| 161 |
+
done = result["done"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
|
| 163 |
+
# Get final results
|
| 164 |
+
final = requests.get(f"{API}/result/{episode_id}").json()
|
| 165 |
+
print(f"Final Score: {final['final_score']}")
|
| 166 |
+
```
|
| 167 |
+
|
| 168 |
+
---
|
| 169 |
+
|
| 170 |
+
## 📂 Project Structure
|
| 171 |
+
|
| 172 |
+
```text
|
| 173 |
+
open-ev-code-handler/
|
| 174 |
+
├── app.py # FastAPI application (9 endpoints)
|
| 175 |
+
├── codelens_env/ # Core evaluation logic
|
| 176 |
+
│ ├── database.py # SQLModel persistence layer
|
| 177 |
+
│ ├── env.py # Episode state machine
|
| 178 |
+
│ ├── models.py # Pydantic v2 data models
|
| 179 |
+
│ ├── scenarios.py # 30 Synthetic PR scenarios
|
| 180 |
+
│ └── graders/ # Grader implementations (Bug, Sec, Arch)
|
| 181 |
+
├── scripts/ # CLI tools (baseline, evaluate, migrate)
|
| 182 |
+
├── static/ # Compiled dashboard assets
|
| 183 |
+
├── tests/ # 155+ Parametrized tests
|
| 184 |
+
├── Dockerfile # Multi-stage, non-root build
|
| 185 |
+
├── docker-compose.yml # Production orchestration
|
| 186 |
+
└── codelens.yaml # CodeLens v2 specification
|
| 187 |
+
```
|
| 188 |
|
| 189 |
---
|
| 190 |
|
| 191 |
+
## 🛠️ Development
|
| 192 |
|
| 193 |
```bash
|
| 194 |
+
# Setup
|
| 195 |
+
python -m venv venv && source venv/bin/activate
|
| 196 |
+
pip install -r requirements.txt
|
| 197 |
+
|
| 198 |
+
# Automated Tests
|
| 199 |
+
PYTHONPATH=. pytest tests/ -v --cov=codelens_env
|
| 200 |
+
|
| 201 |
+
# Linter Check
|
| 202 |
+
pylint codelens_env/ app.py
|
| 203 |
+
|
| 204 |
+
# Scenario Sanity Check
|
| 205 |
+
PYTHONPATH=. python scripts/validate.py
|
| 206 |
```
|
| 207 |
|
| 208 |
+
## 👥 Authors & Maintainers
|
| 209 |
+
|
| 210 |
+
CodeLens is authored and maintained by:
|
| 211 |
+
|
| 212 |
+
- **Arsh Verma** — [GitHub](https://github.com/ArshVermaGit)
|
| 213 |
+
- **Divyansh Rawat** — [GitHub](https://github.com/DsThakurRawat)
|
| 214 |
|
| 215 |
---
|
| 216 |
|
| 217 |
+
## 📄 Contributing & License
|
| 218 |
+
|
| 219 |
+
Please see **[CONTRIBUTING.md](CONTRIBUTING.md)** for details on authoring new scenarios and submission standards.
|
| 220 |
|
| 221 |
+
This project is licensed under the **[MIT License](LICENSE)**.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
CHANGED
|
@@ -42,9 +42,13 @@ logger = logging.getLogger("codelens_env")
|
|
| 42 |
@asynccontextmanager
|
| 43 |
async def lifespan(app: FastAPI):
|
| 44 |
# Startup
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
cleanup_task = asyncio.create_task(cleanup_expired_episodes())
|
| 47 |
-
logger.info(f"CodeLens API started — DB at {settings.db_path}")
|
| 48 |
|
| 49 |
yield
|
| 50 |
|
|
@@ -68,7 +72,20 @@ app = FastAPI(
|
|
| 68 |
lifespan=lifespan,
|
| 69 |
)
|
| 70 |
|
| 71 |
-
# ──
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
app.add_middleware(
|
| 73 |
CORSMiddleware,
|
| 74 |
allow_origins=["*"] if settings.app_env == "development" else [f"http://localhost:{settings.app_port}"],
|
|
@@ -77,7 +94,18 @@ app.add_middleware(
|
|
| 77 |
allow_headers=["*"],
|
| 78 |
)
|
| 79 |
|
| 80 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
limiter = Limiter(key_func=get_remote_address, default_limits=[f"{settings.rate_limit_per_minute}/minute"])
|
| 82 |
app.state.limiter = limiter
|
| 83 |
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
|
|
|
| 42 |
@asynccontextmanager
|
| 43 |
async def lifespan(app: FastAPI):
|
| 44 |
# Startup
|
| 45 |
+
if not os.getenv("TESTING"):
|
| 46 |
+
create_db_and_tables()
|
| 47 |
+
logger.info(f"CodeLens API started — DB at {settings.db_path}")
|
| 48 |
+
else:
|
| 49 |
+
logger.info("CodeLens API running in TESTING mode — DB initialization skipped")
|
| 50 |
+
|
| 51 |
cleanup_task = asyncio.create_task(cleanup_expired_episodes())
|
|
|
|
| 52 |
|
| 53 |
yield
|
| 54 |
|
|
|
|
| 72 |
lifespan=lifespan,
|
| 73 |
)
|
| 74 |
|
| 75 |
+
# ── Security & Middleware ──────────────────────────────────────────────────
|
| 76 |
+
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
| 77 |
+
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
| 78 |
+
|
| 79 |
+
# 1. Trusted Host (Prevent Host-header injection)
|
| 80 |
+
app.add_middleware(
|
| 81 |
+
TrustedHostMiddleware,
|
| 82 |
+
allowed_hosts=["*"] if settings.app_env in ("development", "test") else [f"localhost", "127.0.0.1", "*.github.io", "testserver"]
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
# 2. Proxy Headers (Support Docker/Reverse-proxy)
|
| 86 |
+
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
|
| 87 |
+
|
| 88 |
+
# 3. CORS
|
| 89 |
app.add_middleware(
|
| 90 |
CORSMiddleware,
|
| 91 |
allow_origins=["*"] if settings.app_env == "development" else [f"http://localhost:{settings.app_port}"],
|
|
|
|
| 94 |
allow_headers=["*"],
|
| 95 |
)
|
| 96 |
|
| 97 |
+
# 4. Security Headers Middleware
|
| 98 |
+
@app.middleware("http")
|
| 99 |
+
async def add_security_headers(request: Request, call_next):
|
| 100 |
+
response = await call_next(request)
|
| 101 |
+
response.headers["X-Content-Type-Options"] = "nosniff"
|
| 102 |
+
response.headers["X-Frame-Options"] = "DENY"
|
| 103 |
+
response.headers["X-XSS-Protection"] = "1; mode=block"
|
| 104 |
+
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
|
| 105 |
+
response.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ws: wss:;"
|
| 106 |
+
return response
|
| 107 |
+
|
| 108 |
+
# 5. Rate Limiting
|
| 109 |
limiter = Limiter(key_func=get_remote_address, default_limits=[f"{settings.rate_limit_per_minute}/minute"])
|
| 110 |
app.state.limiter = limiter
|
| 111 |
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
codelens.yaml
CHANGED
|
@@ -1,54 +1,77 @@
|
|
| 1 |
-
version: "1.0"
|
| 2 |
-
name: "
|
|
|
|
| 3 |
description: >
|
| 4 |
-
AI Senior Code Reviewer evaluation environment
|
| 5 |
-
|
| 6 |
-
|
|
|
|
| 7 |
entry_point: "app:app"
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
tags:
|
| 12 |
-
- codelens
|
| 13 |
-
- code-review
|
| 14 |
-
- security
|
| 15 |
-
- software-engineering
|
| 16 |
|
| 17 |
tasks:
|
| 18 |
- id: "bug_detection"
|
| 19 |
-
|
| 20 |
max_steps: 10
|
| 21 |
scenarios: 10
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
|
| 27 |
- id: "security_audit"
|
| 28 |
-
|
| 29 |
max_steps: 15
|
| 30 |
scenarios: 10
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
|
| 36 |
- id: "architectural_review"
|
| 37 |
-
|
| 38 |
max_steps: 20
|
| 39 |
scenarios: 10
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
grading:
|
| 47 |
type: "deterministic"
|
| 48 |
-
|
|
|
|
| 49 |
coverage_weight: 0.4
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: "1.0.0"
|
| 2 |
+
name: "agentorg-codereview"
|
| 3 |
+
owners: ["Arsh Verma", "Divyansh Rawat"]
|
| 4 |
description: >
|
| 5 |
+
AI Senior Code Reviewer evaluation environment for CodeLens.
|
| 6 |
+
Benchmarks agents on 30 synthetic pull requests across Bug Detection,
|
| 7 |
+
Security Audit, and Architectural Review tasks.
|
| 8 |
+
|
| 9 |
entry_point: "app:app"
|
| 10 |
+
dashboard: "/dashboard"
|
| 11 |
+
api_docs: "/docs"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
tasks:
|
| 14 |
- id: "bug_detection"
|
| 15 |
+
description: "Identify logical errors and edge cases in Python code"
|
| 16 |
max_steps: 10
|
| 17 |
scenarios: 10
|
| 18 |
+
difficulty_distribution:
|
| 19 |
+
easy: 2
|
| 20 |
+
medium: 6
|
| 21 |
+
hard: 2
|
| 22 |
|
| 23 |
- id: "security_audit"
|
| 24 |
+
description: "Detect OWASP Top 10 vulnerabilities in Python code"
|
| 25 |
max_steps: 15
|
| 26 |
scenarios: 10
|
| 27 |
+
difficulty_distribution:
|
| 28 |
+
easy: 1
|
| 29 |
+
medium: 7
|
| 30 |
+
hard: 2
|
| 31 |
|
| 32 |
- id: "architectural_review"
|
| 33 |
+
description: "Evaluate design patterns, coupling, and system constraints"
|
| 34 |
max_steps: 20
|
| 35 |
scenarios: 10
|
| 36 |
+
difficulty_distribution:
|
| 37 |
+
easy: 0
|
| 38 |
+
medium: 7
|
| 39 |
+
hard: 3
|
| 40 |
+
|
| 41 |
+
environment:
|
| 42 |
+
noise_budget: 5
|
| 43 |
+
line_tolerance_bug: 3
|
| 44 |
+
line_tolerance_arch: 5
|
| 45 |
+
keyword_match: "any" # agent body must contain ANY listed keyword
|
| 46 |
+
case_sensitive: false
|
| 47 |
|
| 48 |
grading:
|
| 49 |
type: "deterministic"
|
| 50 |
+
|
| 51 |
+
bug_detection:
|
| 52 |
coverage_weight: 0.4
|
| 53 |
+
avg_issue_score_weight: 0.6
|
| 54 |
+
issue_score:
|
| 55 |
+
keyword_weight: 0.5
|
| 56 |
+
severity_weight: 0.5
|
| 57 |
+
false_positive_penalty: 0.1
|
| 58 |
+
|
| 59 |
+
security_audit:
|
| 60 |
+
formula: "avg_issue_score"
|
| 61 |
+
issue_score:
|
| 62 |
+
severity_weight: 0.7
|
| 63 |
+
keyword_weight: 0.3
|
| 64 |
+
severity_scale:
|
| 65 |
+
critical: 4
|
| 66 |
+
high: 3
|
| 67 |
+
medium: 2
|
| 68 |
+
low: 1
|
| 69 |
+
info: 0
|
| 70 |
+
severity_penalty_per_level: 0.3
|
| 71 |
|
| 72 |
+
architectural_review:
|
| 73 |
+
issue_detection_weight: 0.6
|
| 74 |
+
verdict_weight: 0.2
|
| 75 |
+
quality_weight: 0.2
|
| 76 |
+
quality_min_body_length: 20
|
| 77 |
+
quality_max_body_length: 200
|
codelens_env/config.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
from functools import lru_cache
|
|
|
|
| 2 |
from pydantic_settings import BaseSettings, SettingsConfigDict
|
| 3 |
|
| 4 |
class Settings(BaseSettings):
|
|
@@ -19,6 +20,7 @@ class Settings(BaseSettings):
|
|
| 19 |
rate_limit_per_minute: int = 60 # requests per minute per IP
|
| 20 |
|
| 21 |
# Persistence
|
|
|
|
| 22 |
db_path: str = "./data/codelens.db"
|
| 23 |
db_echo: bool = False # Set True to log all SQL queries
|
| 24 |
|
|
|
|
| 1 |
from functools import lru_cache
|
| 2 |
+
from typing import Optional
|
| 3 |
from pydantic_settings import BaseSettings, SettingsConfigDict
|
| 4 |
|
| 5 |
class Settings(BaseSettings):
|
|
|
|
| 20 |
rate_limit_per_minute: int = 60 # requests per minute per IP
|
| 21 |
|
| 22 |
# Persistence
|
| 23 |
+
database_url: Optional[str] = None
|
| 24 |
db_path: str = "./data/codelens.db"
|
| 25 |
db_echo: bool = False # Set True to log all SQL queries
|
| 26 |
|
codelens_env/database.py
CHANGED
|
@@ -7,6 +7,20 @@ from codelens_env.models import EpisodeResult, TaskId
|
|
| 7 |
|
| 8 |
def get_engine():
|
| 9 |
settings = get_settings()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
Path(settings.db_path).parent.mkdir(parents=True, exist_ok=True)
|
| 11 |
return create_engine(
|
| 12 |
f"sqlite:///{settings.db_path}",
|
|
|
|
| 7 |
|
| 8 |
def get_engine():
|
| 9 |
settings = get_settings()
|
| 10 |
+
|
| 11 |
+
if settings.database_url:
|
| 12 |
+
# Support Render/Heroku 'postgres://' URLs by converting to 'postgresql://'
|
| 13 |
+
url = settings.database_url
|
| 14 |
+
if url.startswith("postgres://"):
|
| 15 |
+
url = url.replace("postgres://", "postgresql://", 1)
|
| 16 |
+
|
| 17 |
+
return create_engine(
|
| 18 |
+
url,
|
| 19 |
+
echo=settings.db_echo,
|
| 20 |
+
pool_pre_ping=True, # Ensure connections are alive
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
# Fallback to local SQLite
|
| 24 |
Path(settings.db_path).parent.mkdir(parents=True, exist_ok=True)
|
| 25 |
return create_engine(
|
| 26 |
f"sqlite:///{settings.db_path}",
|
dashboard/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
| 1 |
{
|
|
|
|
| 2 |
"name": "codelens-dashboard",
|
|
|
|
| 3 |
"private": true,
|
| 4 |
-
"
|
| 5 |
"type": "module",
|
| 6 |
"scripts": {
|
| 7 |
"dev": "vite",
|
|
@@ -10,21 +12,21 @@
|
|
| 10 |
},
|
| 11 |
"dependencies": {
|
| 12 |
"@fontsource/inter": "^5.1.0",
|
| 13 |
-
"@tanstack/react-query": "^5.
|
| 14 |
-
"clsx": "^2.1.
|
| 15 |
"lucide-react": "^0.475.0",
|
| 16 |
-
"react": "^
|
| 17 |
-
"react-dom": "^
|
| 18 |
-
"zustand": "^5.0.
|
| 19 |
},
|
| 20 |
"devDependencies": {
|
| 21 |
-
"@tailwindcss/vite": "^4.
|
| 22 |
-
"@types/node": "^
|
| 23 |
-
"@types/react": "^
|
| 24 |
-
"@types/react-dom": "^
|
| 25 |
-
"@vitejs/plugin-react": "^4.3.
|
| 26 |
-
"tailwindcss": "^4.
|
| 27 |
"typescript": "^5.7.0",
|
| 28 |
-
"vite": "^6.
|
| 29 |
}
|
| 30 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"$schema": "https://json.schemastore.org/package",
|
| 3 |
"name": "codelens-dashboard",
|
| 4 |
+
"version": "1.0.0",
|
| 5 |
"private": true,
|
| 6 |
+
"author": "Arsh Verma, Divyansh Rawat",
|
| 7 |
"type": "module",
|
| 8 |
"scripts": {
|
| 9 |
"dev": "vite",
|
|
|
|
| 12 |
},
|
| 13 |
"dependencies": {
|
| 14 |
"@fontsource/inter": "^5.1.0",
|
| 15 |
+
"@tanstack/react-query": "^5.62.0",
|
| 16 |
+
"clsx": "^2.1.1",
|
| 17 |
"lucide-react": "^0.475.0",
|
| 18 |
+
"react": "^19.0.0",
|
| 19 |
+
"react-dom": "^19.0.0",
|
| 20 |
+
"zustand": "^5.0.3"
|
| 21 |
},
|
| 22 |
"devDependencies": {
|
| 23 |
+
"@tailwindcss/vite": "^4.0.0",
|
| 24 |
+
"@types/node": "^22.10.0",
|
| 25 |
+
"@types/react": "^19.0.0",
|
| 26 |
+
"@types/react-dom": "^19.0.0",
|
| 27 |
+
"@vitejs/plugin-react": "^4.3.4",
|
| 28 |
+
"tailwindcss": "^4.0.0",
|
| 29 |
"typescript": "^5.7.0",
|
| 30 |
+
"vite": "^6.0.0"
|
| 31 |
}
|
| 32 |
}
|
dashboard/tsconfig.json
CHANGED
|
@@ -1,16 +1,28 @@
|
|
| 1 |
{
|
|
|
|
| 2 |
"compilerOptions": {
|
| 3 |
-
"
|
| 4 |
-
"
|
|
|
|
|
|
|
|
|
|
| 5 |
"module": "ESNext",
|
|
|
|
|
|
|
|
|
|
| 6 |
"moduleResolution": "bundler",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
"jsx": "react-jsx",
|
|
|
|
|
|
|
| 8 |
"strict": true,
|
| 9 |
"noUnusedLocals": true,
|
| 10 |
"noUnusedParameters": true,
|
| 11 |
-
"noFallthroughCasesInSwitch": true
|
| 12 |
-
"skipLibCheck": true,
|
| 13 |
-
"types": ["vite/client", "node"]
|
| 14 |
},
|
| 15 |
"include": ["src"]
|
| 16 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"$schema": "https://json.schemastore.org/tsconfig",
|
| 3 |
"compilerOptions": {
|
| 4 |
+
"composite": true,
|
| 5 |
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
| 6 |
+
"target": "ES2020",
|
| 7 |
+
"useDefineForClassFields": true,
|
| 8 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
| 9 |
"module": "ESNext",
|
| 10 |
+
"skipLibCheck": true,
|
| 11 |
+
|
| 12 |
+
/* Bundler mode */
|
| 13 |
"moduleResolution": "bundler",
|
| 14 |
+
"allowImportingTsExtensions": true,
|
| 15 |
+
"resolveJsonModule": true,
|
| 16 |
+
"isolatedModules": true,
|
| 17 |
+
"moduleDetection": "force",
|
| 18 |
+
"noEmit": true,
|
| 19 |
"jsx": "react-jsx",
|
| 20 |
+
|
| 21 |
+
/* Linting */
|
| 22 |
"strict": true,
|
| 23 |
"noUnusedLocals": true,
|
| 24 |
"noUnusedParameters": true,
|
| 25 |
+
"noFallthroughCasesInSwitch": true
|
|
|
|
|
|
|
| 26 |
},
|
| 27 |
"include": ["src"]
|
| 28 |
}
|
dashboard/vercel.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"rewrites": [
|
| 3 |
+
{
|
| 4 |
+
"source": "/api/(.*)",
|
| 5 |
+
"destination": "https://YOUR_BACKEND_URL.render.com/$1"
|
| 6 |
+
},
|
| 7 |
+
{
|
| 8 |
+
"source": "/ws/(.*)",
|
| 9 |
+
"destination": "wss://YOUR_BACKEND_URL.render.com/ws/$1"
|
| 10 |
+
}
|
| 11 |
+
],
|
| 12 |
+
"framework": "vite",
|
| 13 |
+
"buildCommand": "npm run build",
|
| 14 |
+
"outputDirectory": "dist"
|
| 15 |
+
}
|
docker-compose.test.yml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: "3.9"
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
test:
|
| 5 |
+
build:
|
| 6 |
+
context: .
|
| 7 |
+
target: builder
|
| 8 |
+
container_name: codelens-test
|
| 9 |
+
command: >
|
| 10 |
+
sh -c "/build/venv/bin/pip install pytest pytest-cov &&
|
| 11 |
+
PYTHONPATH=/app /build/venv/bin/python -m pytest tests/ -v --tb=short"
|
| 12 |
+
volumes:
|
| 13 |
+
- .:/app
|
| 14 |
+
environment:
|
| 15 |
+
- APP_ENV=test
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: "3.9"
|
| 2 |
+
|
| 3 |
+
services:
|
| 4 |
+
api:
|
| 5 |
+
build:
|
| 6 |
+
context: .
|
| 7 |
+
target: production
|
| 8 |
+
container_name: codelens-api
|
| 9 |
+
ports:
|
| 10 |
+
- "${APP_PORT:-7860}:7860"
|
| 11 |
+
env_file:
|
| 12 |
+
- .env
|
| 13 |
+
environment:
|
| 14 |
+
- APP_ENV=${APP_ENV:-development}
|
| 15 |
+
- LOG_LEVEL=${LOG_LEVEL:-INFO}
|
| 16 |
+
volumes:
|
| 17 |
+
- codelens-data:/app/data
|
| 18 |
+
restart: unless-stopped
|
| 19 |
+
healthcheck:
|
| 20 |
+
test: ["CMD", "curl", "-f", "http://localhost:7860/health"]
|
| 21 |
+
interval: 30s
|
| 22 |
+
timeout: 10s
|
| 23 |
+
retries: 3
|
| 24 |
+
start_period: 15s
|
| 25 |
+
|
| 26 |
+
volumes:
|
| 27 |
+
codelens-data:
|
| 28 |
+
driver: local
|
requirements.txt
CHANGED
|
@@ -1,15 +1,16 @@
|
|
| 1 |
-
fastapi>=0.
|
| 2 |
-
uvicorn>=0.
|
| 3 |
-
pydantic>=2.
|
| 4 |
pytest>=8.0.0
|
| 5 |
-
requests>=2.
|
| 6 |
-
websockets>=
|
| 7 |
-
httpx
|
| 8 |
-
openai>=1.
|
| 9 |
-
pydantic-settings=
|
| 10 |
-
slowapi=
|
| 11 |
-
python-dotenv=
|
| 12 |
-
sqlmodel=
|
| 13 |
-
aiosqlite=
|
| 14 |
-
pytest-cov=
|
| 15 |
-
aiofiles=
|
|
|
|
|
|
| 1 |
+
fastapi>=0.115.0
|
| 2 |
+
uvicorn>=0.30.0
|
| 3 |
+
pydantic>=2.10.0
|
| 4 |
pytest>=8.0.0
|
| 5 |
+
requests>=2.32.0
|
| 6 |
+
websockets>=13.0
|
| 7 |
+
httpx>=0.28.0
|
| 8 |
+
openai>=1.50.0
|
| 9 |
+
pydantic-settings>=2.6.0
|
| 10 |
+
slowapi>=0.1.9
|
| 11 |
+
python-dotenv>=1.0.1
|
| 12 |
+
sqlmodel>=0.0.22
|
| 13 |
+
aiosqlite>=0.20.0
|
| 14 |
+
pytest-cov>=5.0.0
|
| 15 |
+
aiofiles>=24.0.0
|
| 16 |
+
PyYAML>=6.0.1
|
results.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
[
|
| 2 |
{
|
| 3 |
-
"episode_id": "
|
| 4 |
"task_id": "bug_detection",
|
| 5 |
"seed": 0,
|
| 6 |
"final_score": 0.0,
|
|
@@ -9,10 +9,10 @@
|
|
| 9 |
"issues_total": 1,
|
| 10 |
"noise_penalties": 0,
|
| 11 |
"terminated_reason": "terminal_action",
|
| 12 |
-
"duration_seconds": 0.
|
| 13 |
},
|
| 14 |
{
|
| 15 |
-
"episode_id": "
|
| 16 |
"task_id": "bug_detection",
|
| 17 |
"seed": 1,
|
| 18 |
"final_score": 0.0,
|
|
@@ -24,7 +24,7 @@
|
|
| 24 |
"duration_seconds": 0.02
|
| 25 |
},
|
| 26 |
{
|
| 27 |
-
"episode_id": "
|
| 28 |
"task_id": "bug_detection",
|
| 29 |
"seed": 2,
|
| 30 |
"final_score": 0.9167,
|
|
@@ -33,10 +33,10 @@
|
|
| 33 |
"issues_total": 1,
|
| 34 |
"noise_penalties": 5,
|
| 35 |
"terminated_reason": "noise_exhausted",
|
| 36 |
-
"duration_seconds": 0.
|
| 37 |
},
|
| 38 |
{
|
| 39 |
-
"episode_id": "
|
| 40 |
"task_id": "bug_detection",
|
| 41 |
"seed": 3,
|
| 42 |
"final_score": 0.9167,
|
|
@@ -45,10 +45,10 @@
|
|
| 45 |
"issues_total": 1,
|
| 46 |
"noise_penalties": 5,
|
| 47 |
"terminated_reason": "noise_exhausted",
|
| 48 |
-
"duration_seconds": 0.
|
| 49 |
},
|
| 50 |
{
|
| 51 |
-
"episode_id": "
|
| 52 |
"task_id": "bug_detection",
|
| 53 |
"seed": 4,
|
| 54 |
"final_score": 0.8267,
|
|
@@ -60,7 +60,7 @@
|
|
| 60 |
"duration_seconds": 0.03
|
| 61 |
},
|
| 62 |
{
|
| 63 |
-
"episode_id": "
|
| 64 |
"task_id": "bug_detection",
|
| 65 |
"seed": 5,
|
| 66 |
"final_score": 0.0,
|
|
@@ -69,10 +69,10 @@
|
|
| 69 |
"issues_total": 1,
|
| 70 |
"noise_penalties": 0,
|
| 71 |
"terminated_reason": "terminal_action",
|
| 72 |
-
"duration_seconds": 0.
|
| 73 |
},
|
| 74 |
{
|
| 75 |
-
"episode_id": "
|
| 76 |
"task_id": "bug_detection",
|
| 77 |
"seed": 6,
|
| 78 |
"final_score": 0.0,
|
|
@@ -84,7 +84,7 @@
|
|
| 84 |
"duration_seconds": 0.02
|
| 85 |
},
|
| 86 |
{
|
| 87 |
-
"episode_id": "
|
| 88 |
"task_id": "bug_detection",
|
| 89 |
"seed": 7,
|
| 90 |
"final_score": 0.0,
|
|
@@ -96,7 +96,7 @@
|
|
| 96 |
"duration_seconds": 0.02
|
| 97 |
},
|
| 98 |
{
|
| 99 |
-
"episode_id": "
|
| 100 |
"task_id": "bug_detection",
|
| 101 |
"seed": 8,
|
| 102 |
"final_score": 0.9167,
|
|
@@ -105,10 +105,10 @@
|
|
| 105 |
"issues_total": 1,
|
| 106 |
"noise_penalties": 5,
|
| 107 |
"terminated_reason": "noise_exhausted",
|
| 108 |
-
"duration_seconds": 0.
|
| 109 |
},
|
| 110 |
{
|
| 111 |
-
"episode_id": "
|
| 112 |
"task_id": "bug_detection",
|
| 113 |
"seed": 9,
|
| 114 |
"final_score": 0.0,
|
|
@@ -120,7 +120,7 @@
|
|
| 120 |
"duration_seconds": 0.03
|
| 121 |
},
|
| 122 |
{
|
| 123 |
-
"episode_id": "
|
| 124 |
"task_id": "security_audit",
|
| 125 |
"seed": 0,
|
| 126 |
"final_score": 0.0,
|
|
@@ -132,7 +132,7 @@
|
|
| 132 |
"duration_seconds": 0.03
|
| 133 |
},
|
| 134 |
{
|
| 135 |
-
"episode_id": "
|
| 136 |
"task_id": "security_audit",
|
| 137 |
"seed": 1,
|
| 138 |
"final_score": 1.0,
|
|
@@ -141,10 +141,10 @@
|
|
| 141 |
"issues_total": 1,
|
| 142 |
"noise_penalties": 5,
|
| 143 |
"terminated_reason": "noise_exhausted",
|
| 144 |
-
"duration_seconds": 0.
|
| 145 |
},
|
| 146 |
{
|
| 147 |
-
"episode_id": "
|
| 148 |
"task_id": "security_audit",
|
| 149 |
"seed": 2,
|
| 150 |
"final_score": 0.0,
|
|
@@ -156,7 +156,7 @@
|
|
| 156 |
"duration_seconds": 0.03
|
| 157 |
},
|
| 158 |
{
|
| 159 |
-
"episode_id": "
|
| 160 |
"task_id": "security_audit",
|
| 161 |
"seed": 3,
|
| 162 |
"final_score": 0.85,
|
|
@@ -165,10 +165,10 @@
|
|
| 165 |
"issues_total": 1,
|
| 166 |
"noise_penalties": 5,
|
| 167 |
"terminated_reason": "noise_exhausted",
|
| 168 |
-
"duration_seconds": 0.
|
| 169 |
},
|
| 170 |
{
|
| 171 |
-
"episode_id": "
|
| 172 |
"task_id": "security_audit",
|
| 173 |
"seed": 4,
|
| 174 |
"final_score": 0.0,
|
|
@@ -180,7 +180,7 @@
|
|
| 180 |
"duration_seconds": 0.03
|
| 181 |
},
|
| 182 |
{
|
| 183 |
-
"episode_id": "
|
| 184 |
"task_id": "security_audit",
|
| 185 |
"seed": 5,
|
| 186 |
"final_score": 0.0,
|
|
@@ -189,10 +189,10 @@
|
|
| 189 |
"issues_total": 1,
|
| 190 |
"noise_penalties": 5,
|
| 191 |
"terminated_reason": "noise_exhausted",
|
| 192 |
-
"duration_seconds": 0.
|
| 193 |
},
|
| 194 |
{
|
| 195 |
-
"episode_id": "
|
| 196 |
"task_id": "security_audit",
|
| 197 |
"seed": 6,
|
| 198 |
"final_score": 0.0,
|
|
@@ -204,7 +204,7 @@
|
|
| 204 |
"duration_seconds": 0.03
|
| 205 |
},
|
| 206 |
{
|
| 207 |
-
"episode_id": "
|
| 208 |
"task_id": "security_audit",
|
| 209 |
"seed": 7,
|
| 210 |
"final_score": 0.0,
|
|
@@ -216,7 +216,7 @@
|
|
| 216 |
"duration_seconds": 0.03
|
| 217 |
},
|
| 218 |
{
|
| 219 |
-
"episode_id": "
|
| 220 |
"task_id": "security_audit",
|
| 221 |
"seed": 8,
|
| 222 |
"final_score": 0.0,
|
|
@@ -225,10 +225,10 @@
|
|
| 225 |
"issues_total": 1,
|
| 226 |
"noise_penalties": 5,
|
| 227 |
"terminated_reason": "noise_exhausted",
|
| 228 |
-
"duration_seconds": 0.
|
| 229 |
},
|
| 230 |
{
|
| 231 |
-
"episode_id": "
|
| 232 |
"task_id": "security_audit",
|
| 233 |
"seed": 9,
|
| 234 |
"final_score": 0.0,
|
|
@@ -240,7 +240,7 @@
|
|
| 240 |
"duration_seconds": 0.03
|
| 241 |
},
|
| 242 |
{
|
| 243 |
-
"episode_id": "
|
| 244 |
"task_id": "architectural_review",
|
| 245 |
"seed": 0,
|
| 246 |
"final_score": 0.0,
|
|
@@ -249,10 +249,10 @@
|
|
| 249 |
"issues_total": 1,
|
| 250 |
"noise_penalties": 0,
|
| 251 |
"terminated_reason": "terminal_action",
|
| 252 |
-
"duration_seconds": 0.
|
| 253 |
},
|
| 254 |
{
|
| 255 |
-
"episode_id": "
|
| 256 |
"task_id": "architectural_review",
|
| 257 |
"seed": 1,
|
| 258 |
"final_score": 0.059,
|
|
@@ -261,10 +261,10 @@
|
|
| 261 |
"issues_total": 1,
|
| 262 |
"noise_penalties": 5,
|
| 263 |
"terminated_reason": "noise_exhausted",
|
| 264 |
-
"duration_seconds": 0.
|
| 265 |
},
|
| 266 |
{
|
| 267 |
-
"episode_id": "
|
| 268 |
"task_id": "architectural_review",
|
| 269 |
"seed": 2,
|
| 270 |
"final_score": 0.661,
|
|
@@ -273,10 +273,10 @@
|
|
| 273 |
"issues_total": 1,
|
| 274 |
"noise_penalties": 5,
|
| 275 |
"terminated_reason": "noise_exhausted",
|
| 276 |
-
"duration_seconds": 0.
|
| 277 |
},
|
| 278 |
{
|
| 279 |
-
"episode_id": "
|
| 280 |
"task_id": "architectural_review",
|
| 281 |
"seed": 3,
|
| 282 |
"final_score": 0.658,
|
|
@@ -288,7 +288,7 @@
|
|
| 288 |
"duration_seconds": 0.03
|
| 289 |
},
|
| 290 |
{
|
| 291 |
-
"episode_id": "
|
| 292 |
"task_id": "architectural_review",
|
| 293 |
"seed": 4,
|
| 294 |
"final_score": 0.058,
|
|
@@ -300,7 +300,7 @@
|
|
| 300 |
"duration_seconds": 0.03
|
| 301 |
},
|
| 302 |
{
|
| 303 |
-
"episode_id": "
|
| 304 |
"task_id": "architectural_review",
|
| 305 |
"seed": 5,
|
| 306 |
"final_score": 0.657,
|
|
@@ -309,10 +309,10 @@
|
|
| 309 |
"issues_total": 1,
|
| 310 |
"noise_penalties": 5,
|
| 311 |
"terminated_reason": "noise_exhausted",
|
| 312 |
-
"duration_seconds": 0.
|
| 313 |
},
|
| 314 |
{
|
| 315 |
-
"episode_id": "
|
| 316 |
"task_id": "architectural_review",
|
| 317 |
"seed": 6,
|
| 318 |
"final_score": 0.059,
|
|
@@ -324,7 +324,7 @@
|
|
| 324 |
"duration_seconds": 0.03
|
| 325 |
},
|
| 326 |
{
|
| 327 |
-
"episode_id": "
|
| 328 |
"task_id": "architectural_review",
|
| 329 |
"seed": 7,
|
| 330 |
"final_score": 0.664,
|
|
@@ -333,10 +333,10 @@
|
|
| 333 |
"issues_total": 1,
|
| 334 |
"noise_penalties": 5,
|
| 335 |
"terminated_reason": "noise_exhausted",
|
| 336 |
-
"duration_seconds": 0.
|
| 337 |
},
|
| 338 |
{
|
| 339 |
-
"episode_id": "
|
| 340 |
"task_id": "architectural_review",
|
| 341 |
"seed": 8,
|
| 342 |
"final_score": 0.039,
|
|
@@ -345,10 +345,10 @@
|
|
| 345 |
"issues_total": 1,
|
| 346 |
"noise_penalties": 5,
|
| 347 |
"terminated_reason": "noise_exhausted",
|
| 348 |
-
"duration_seconds": 0.
|
| 349 |
},
|
| 350 |
{
|
| 351 |
-
"episode_id": "
|
| 352 |
"task_id": "architectural_review",
|
| 353 |
"seed": 9,
|
| 354 |
"final_score": 0.075,
|
|
@@ -357,6 +357,6 @@
|
|
| 357 |
"issues_total": 1,
|
| 358 |
"noise_penalties": 5,
|
| 359 |
"terminated_reason": "noise_exhausted",
|
| 360 |
-
"duration_seconds": 0.
|
| 361 |
}
|
| 362 |
]
|
|
|
|
| 1 |
[
|
| 2 |
{
|
| 3 |
+
"episode_id": "27c65a00-9315-4d7d-b457-f8d64b0da466",
|
| 4 |
"task_id": "bug_detection",
|
| 5 |
"seed": 0,
|
| 6 |
"final_score": 0.0,
|
|
|
|
| 9 |
"issues_total": 1,
|
| 10 |
"noise_penalties": 0,
|
| 11 |
"terminated_reason": "terminal_action",
|
| 12 |
+
"duration_seconds": 0.02
|
| 13 |
},
|
| 14 |
{
|
| 15 |
+
"episode_id": "8bdcee56-8a56-4ec8-88fc-a70e78ab48f2",
|
| 16 |
"task_id": "bug_detection",
|
| 17 |
"seed": 1,
|
| 18 |
"final_score": 0.0,
|
|
|
|
| 24 |
"duration_seconds": 0.02
|
| 25 |
},
|
| 26 |
{
|
| 27 |
+
"episode_id": "d3dcda88-ce7e-4965-9409-4e97c98cf444",
|
| 28 |
"task_id": "bug_detection",
|
| 29 |
"seed": 2,
|
| 30 |
"final_score": 0.9167,
|
|
|
|
| 33 |
"issues_total": 1,
|
| 34 |
"noise_penalties": 5,
|
| 35 |
"terminated_reason": "noise_exhausted",
|
| 36 |
+
"duration_seconds": 0.04
|
| 37 |
},
|
| 38 |
{
|
| 39 |
+
"episode_id": "822dfb7d-d67b-4dc9-9cfe-866665a9e5b9",
|
| 40 |
"task_id": "bug_detection",
|
| 41 |
"seed": 3,
|
| 42 |
"final_score": 0.9167,
|
|
|
|
| 45 |
"issues_total": 1,
|
| 46 |
"noise_penalties": 5,
|
| 47 |
"terminated_reason": "noise_exhausted",
|
| 48 |
+
"duration_seconds": 0.03
|
| 49 |
},
|
| 50 |
{
|
| 51 |
+
"episode_id": "c35c2096-b7b8-4a54-b0c7-d8fa1e1538bf",
|
| 52 |
"task_id": "bug_detection",
|
| 53 |
"seed": 4,
|
| 54 |
"final_score": 0.8267,
|
|
|
|
| 60 |
"duration_seconds": 0.03
|
| 61 |
},
|
| 62 |
{
|
| 63 |
+
"episode_id": "9b4e5191-57da-4b10-b31e-5b8236b9b1f4",
|
| 64 |
"task_id": "bug_detection",
|
| 65 |
"seed": 5,
|
| 66 |
"final_score": 0.0,
|
|
|
|
| 69 |
"issues_total": 1,
|
| 70 |
"noise_penalties": 0,
|
| 71 |
"terminated_reason": "terminal_action",
|
| 72 |
+
"duration_seconds": 0.01
|
| 73 |
},
|
| 74 |
{
|
| 75 |
+
"episode_id": "c1e64a16-512a-4716-b0f9-5d9fe14a142d",
|
| 76 |
"task_id": "bug_detection",
|
| 77 |
"seed": 6,
|
| 78 |
"final_score": 0.0,
|
|
|
|
| 84 |
"duration_seconds": 0.02
|
| 85 |
},
|
| 86 |
{
|
| 87 |
+
"episode_id": "cc8bd066-506e-46de-ba0f-0a446f045945",
|
| 88 |
"task_id": "bug_detection",
|
| 89 |
"seed": 7,
|
| 90 |
"final_score": 0.0,
|
|
|
|
| 96 |
"duration_seconds": 0.02
|
| 97 |
},
|
| 98 |
{
|
| 99 |
+
"episode_id": "20dd7158-a49f-4b6c-adcd-6b76b4274e94",
|
| 100 |
"task_id": "bug_detection",
|
| 101 |
"seed": 8,
|
| 102 |
"final_score": 0.9167,
|
|
|
|
| 105 |
"issues_total": 1,
|
| 106 |
"noise_penalties": 5,
|
| 107 |
"terminated_reason": "noise_exhausted",
|
| 108 |
+
"duration_seconds": 0.03
|
| 109 |
},
|
| 110 |
{
|
| 111 |
+
"episode_id": "14b9d1a5-03b7-45b2-8b23-b6c387bced5b",
|
| 112 |
"task_id": "bug_detection",
|
| 113 |
"seed": 9,
|
| 114 |
"final_score": 0.0,
|
|
|
|
| 120 |
"duration_seconds": 0.03
|
| 121 |
},
|
| 122 |
{
|
| 123 |
+
"episode_id": "2b1c4b7f-cf05-4312-89d2-49883b9ed6c1",
|
| 124 |
"task_id": "security_audit",
|
| 125 |
"seed": 0,
|
| 126 |
"final_score": 0.0,
|
|
|
|
| 132 |
"duration_seconds": 0.03
|
| 133 |
},
|
| 134 |
{
|
| 135 |
+
"episode_id": "47145114-7a99-4c81-baa9-1f9c4221f48a",
|
| 136 |
"task_id": "security_audit",
|
| 137 |
"seed": 1,
|
| 138 |
"final_score": 1.0,
|
|
|
|
| 141 |
"issues_total": 1,
|
| 142 |
"noise_penalties": 5,
|
| 143 |
"terminated_reason": "noise_exhausted",
|
| 144 |
+
"duration_seconds": 0.03
|
| 145 |
},
|
| 146 |
{
|
| 147 |
+
"episode_id": "5d9d3f74-a127-46b2-a331-45f30f0d2f6f",
|
| 148 |
"task_id": "security_audit",
|
| 149 |
"seed": 2,
|
| 150 |
"final_score": 0.0,
|
|
|
|
| 156 |
"duration_seconds": 0.03
|
| 157 |
},
|
| 158 |
{
|
| 159 |
+
"episode_id": "f383afc4-df2d-4a60-abd1-a583cb2de538",
|
| 160 |
"task_id": "security_audit",
|
| 161 |
"seed": 3,
|
| 162 |
"final_score": 0.85,
|
|
|
|
| 165 |
"issues_total": 1,
|
| 166 |
"noise_penalties": 5,
|
| 167 |
"terminated_reason": "noise_exhausted",
|
| 168 |
+
"duration_seconds": 0.03
|
| 169 |
},
|
| 170 |
{
|
| 171 |
+
"episode_id": "df23e18b-c21a-4390-9afa-f77eb1b8050b",
|
| 172 |
"task_id": "security_audit",
|
| 173 |
"seed": 4,
|
| 174 |
"final_score": 0.0,
|
|
|
|
| 180 |
"duration_seconds": 0.03
|
| 181 |
},
|
| 182 |
{
|
| 183 |
+
"episode_id": "bcec83af-4aaf-4997-922a-1556ca63fcf3",
|
| 184 |
"task_id": "security_audit",
|
| 185 |
"seed": 5,
|
| 186 |
"final_score": 0.0,
|
|
|
|
| 189 |
"issues_total": 1,
|
| 190 |
"noise_penalties": 5,
|
| 191 |
"terminated_reason": "noise_exhausted",
|
| 192 |
+
"duration_seconds": 0.03
|
| 193 |
},
|
| 194 |
{
|
| 195 |
+
"episode_id": "65afedd0-9ad1-45f7-9615-dafeaa390338",
|
| 196 |
"task_id": "security_audit",
|
| 197 |
"seed": 6,
|
| 198 |
"final_score": 0.0,
|
|
|
|
| 204 |
"duration_seconds": 0.03
|
| 205 |
},
|
| 206 |
{
|
| 207 |
+
"episode_id": "0e8b65e5-d59c-41fb-8038-1f3b1d4b657b",
|
| 208 |
"task_id": "security_audit",
|
| 209 |
"seed": 7,
|
| 210 |
"final_score": 0.0,
|
|
|
|
| 216 |
"duration_seconds": 0.03
|
| 217 |
},
|
| 218 |
{
|
| 219 |
+
"episode_id": "ae477e21-2718-477e-8a11-e46bda49bea7",
|
| 220 |
"task_id": "security_audit",
|
| 221 |
"seed": 8,
|
| 222 |
"final_score": 0.0,
|
|
|
|
| 225 |
"issues_total": 1,
|
| 226 |
"noise_penalties": 5,
|
| 227 |
"terminated_reason": "noise_exhausted",
|
| 228 |
+
"duration_seconds": 0.04
|
| 229 |
},
|
| 230 |
{
|
| 231 |
+
"episode_id": "0cd3d18c-cdf6-4752-9dc8-cade848cee0e",
|
| 232 |
"task_id": "security_audit",
|
| 233 |
"seed": 9,
|
| 234 |
"final_score": 0.0,
|
|
|
|
| 240 |
"duration_seconds": 0.03
|
| 241 |
},
|
| 242 |
{
|
| 243 |
+
"episode_id": "d9f79316-57ea-42ad-b191-797ea895951b",
|
| 244 |
"task_id": "architectural_review",
|
| 245 |
"seed": 0,
|
| 246 |
"final_score": 0.0,
|
|
|
|
| 249 |
"issues_total": 1,
|
| 250 |
"noise_penalties": 0,
|
| 251 |
"terminated_reason": "terminal_action",
|
| 252 |
+
"duration_seconds": 0.01
|
| 253 |
},
|
| 254 |
{
|
| 255 |
+
"episode_id": "65112da3-d319-4eb3-9774-1c43313fe1ec",
|
| 256 |
"task_id": "architectural_review",
|
| 257 |
"seed": 1,
|
| 258 |
"final_score": 0.059,
|
|
|
|
| 261 |
"issues_total": 1,
|
| 262 |
"noise_penalties": 5,
|
| 263 |
"terminated_reason": "noise_exhausted",
|
| 264 |
+
"duration_seconds": 0.03
|
| 265 |
},
|
| 266 |
{
|
| 267 |
+
"episode_id": "c1d6bd63-1abf-403b-97be-cf1962959910",
|
| 268 |
"task_id": "architectural_review",
|
| 269 |
"seed": 2,
|
| 270 |
"final_score": 0.661,
|
|
|
|
| 273 |
"issues_total": 1,
|
| 274 |
"noise_penalties": 5,
|
| 275 |
"terminated_reason": "noise_exhausted",
|
| 276 |
+
"duration_seconds": 0.03
|
| 277 |
},
|
| 278 |
{
|
| 279 |
+
"episode_id": "fab56551-f2ae-42be-88eb-854ef236b29a",
|
| 280 |
"task_id": "architectural_review",
|
| 281 |
"seed": 3,
|
| 282 |
"final_score": 0.658,
|
|
|
|
| 288 |
"duration_seconds": 0.03
|
| 289 |
},
|
| 290 |
{
|
| 291 |
+
"episode_id": "8f4b8fa9-f281-4bb2-ae71-7ad6c7ca7fc9",
|
| 292 |
"task_id": "architectural_review",
|
| 293 |
"seed": 4,
|
| 294 |
"final_score": 0.058,
|
|
|
|
| 300 |
"duration_seconds": 0.03
|
| 301 |
},
|
| 302 |
{
|
| 303 |
+
"episode_id": "7eaa445d-774d-44b1-80d9-06e978b022df",
|
| 304 |
"task_id": "architectural_review",
|
| 305 |
"seed": 5,
|
| 306 |
"final_score": 0.657,
|
|
|
|
| 309 |
"issues_total": 1,
|
| 310 |
"noise_penalties": 5,
|
| 311 |
"terminated_reason": "noise_exhausted",
|
| 312 |
+
"duration_seconds": 0.03
|
| 313 |
},
|
| 314 |
{
|
| 315 |
+
"episode_id": "103d6e2b-e40d-49ff-8f36-6910d463b48b",
|
| 316 |
"task_id": "architectural_review",
|
| 317 |
"seed": 6,
|
| 318 |
"final_score": 0.059,
|
|
|
|
| 324 |
"duration_seconds": 0.03
|
| 325 |
},
|
| 326 |
{
|
| 327 |
+
"episode_id": "5206e9d8-0ee0-482c-ad3d-34adf2ce57a7",
|
| 328 |
"task_id": "architectural_review",
|
| 329 |
"seed": 7,
|
| 330 |
"final_score": 0.664,
|
|
|
|
| 333 |
"issues_total": 1,
|
| 334 |
"noise_penalties": 5,
|
| 335 |
"terminated_reason": "noise_exhausted",
|
| 336 |
+
"duration_seconds": 0.03
|
| 337 |
},
|
| 338 |
{
|
| 339 |
+
"episode_id": "c147abd7-f887-44a2-b166-5382e292d793",
|
| 340 |
"task_id": "architectural_review",
|
| 341 |
"seed": 8,
|
| 342 |
"final_score": 0.039,
|
|
|
|
| 345 |
"issues_total": 1,
|
| 346 |
"noise_penalties": 5,
|
| 347 |
"terminated_reason": "noise_exhausted",
|
| 348 |
+
"duration_seconds": 0.08
|
| 349 |
},
|
| 350 |
{
|
| 351 |
+
"episode_id": "6fe54153-a878-47d7-a521-5cd70f55f6b8",
|
| 352 |
"task_id": "architectural_review",
|
| 353 |
"seed": 9,
|
| 354 |
"final_score": 0.075,
|
|
|
|
| 357 |
"issues_total": 1,
|
| 358 |
"noise_penalties": 5,
|
| 359 |
"terminated_reason": "noise_exhausted",
|
| 360 |
+
"duration_seconds": 0.09
|
| 361 |
}
|
| 362 |
]
|
scripts/reset_db.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Reset the CodeLens database: deletes the SQLite file and re-initializes tables.
|
| 4 |
+
Useful for clearing test data and starting fresh evaluation benchmarks.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
from pathlib import Path
|
| 10 |
+
|
| 11 |
+
# Add project root to path
|
| 12 |
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
| 13 |
+
|
| 14 |
+
from codelens_env.config import get_settings
|
| 15 |
+
from codelens_env.database import create_db_and_tables
|
| 16 |
+
|
| 17 |
+
def reset_db():
|
| 18 |
+
settings = get_settings()
|
| 19 |
+
db_path = Path(settings.db_path)
|
| 20 |
+
|
| 21 |
+
# 1. Delete existing database file
|
| 22 |
+
if db_path.exists():
|
| 23 |
+
print(f"Removing existing database at: {db_path}")
|
| 24 |
+
try:
|
| 25 |
+
os.remove(db_path)
|
| 26 |
+
print("Successfully deleted old records.")
|
| 27 |
+
except Exception as e:
|
| 28 |
+
print(f"Error deleting file: {e}")
|
| 29 |
+
sys.exit(1)
|
| 30 |
+
else:
|
| 31 |
+
print(f"No existing database found at {db_path}")
|
| 32 |
+
|
| 33 |
+
# 2. Re-initialize
|
| 34 |
+
print(f"Re-initializing schema...")
|
| 35 |
+
try:
|
| 36 |
+
create_db_and_tables()
|
| 37 |
+
print("Database reset successfully. You now have a clean dashboard.")
|
| 38 |
+
except Exception as e:
|
| 39 |
+
print(f"Error re-initializing: {e}")
|
| 40 |
+
sys.exit(1)
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
confirm = input("This will permanently delete all leaderboard and episode data. Proceed? [y/N]: ")
|
| 44 |
+
if confirm.lower() == 'y':
|
| 45 |
+
reset_db()
|
| 46 |
+
else:
|
| 47 |
+
print("Reset aborted.")
|
tests/conftest.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
| 1 |
import pytest
|
|
|
|
|
|
|
|
|
|
| 2 |
from fastapi.testclient import TestClient
|
| 3 |
from sqlmodel import SQLModel, Session, create_engine
|
| 4 |
from sqlmodel.pool import StaticPool
|
|
|
|
| 1 |
import pytest
|
| 2 |
+
import os
|
| 3 |
+
os.environ["TESTING"] = "true"
|
| 4 |
+
os.environ["APP_ENV"] = "test"
|
| 5 |
from fastapi.testclient import TestClient
|
| 6 |
from sqlmodel import SQLModel, Session, create_engine
|
| 7 |
from sqlmodel.pool import StaticPool
|
tests/test_api.py
CHANGED
|
@@ -3,15 +3,13 @@ from fastapi.testclient import TestClient
|
|
| 3 |
from app import app
|
| 4 |
from codelens_env.models import TaskId, ActionType, Category, Severity, Verdict
|
| 5 |
|
| 6 |
-
def test_api_health():
|
| 7 |
-
client = TestClient(app)
|
| 8 |
response = client.get("/health")
|
| 9 |
assert response.status_code == 200
|
| 10 |
assert response.json()["status"] == "ok"
|
| 11 |
assert response.json()["env_ready"] is True
|
| 12 |
|
| 13 |
-
def test_api_workflow():
|
| 14 |
-
client = TestClient(app)
|
| 15 |
|
| 16 |
# 1. Reset
|
| 17 |
reset_resp = client.post("/reset", json={"task_id": "bug_detection", "seed": 1})
|
|
@@ -34,8 +32,7 @@ def test_api_workflow():
|
|
| 34 |
assert result_resp.status_code == 200
|
| 35 |
assert result_resp.json()["final_score"] >= 0
|
| 36 |
|
| 37 |
-
def test_api_leaderboard():
|
| 38 |
-
client = TestClient(app)
|
| 39 |
# Submit a score
|
| 40 |
sub = {
|
| 41 |
"agent_name": "test_agent",
|
|
@@ -55,8 +52,7 @@ def test_api_leaderboard():
|
|
| 55 |
assert len(bug_entries) > 0
|
| 56 |
assert bug_entries[0]["agent_name"] == "test_agent"
|
| 57 |
|
| 58 |
-
def test_api_invalid_episode():
|
| 59 |
-
client = TestClient(app)
|
| 60 |
response = client.post("/step/nonexistent-id", json={
|
| 61 |
"action_type": "comment",
|
| 62 |
"body": "hello"
|
|
|
|
| 3 |
from app import app
|
| 4 |
from codelens_env.models import TaskId, ActionType, Category, Severity, Verdict
|
| 5 |
|
| 6 |
+
def test_api_health(client):
|
|
|
|
| 7 |
response = client.get("/health")
|
| 8 |
assert response.status_code == 200
|
| 9 |
assert response.json()["status"] == "ok"
|
| 10 |
assert response.json()["env_ready"] is True
|
| 11 |
|
| 12 |
+
def test_api_workflow(client):
|
|
|
|
| 13 |
|
| 14 |
# 1. Reset
|
| 15 |
reset_resp = client.post("/reset", json={"task_id": "bug_detection", "seed": 1})
|
|
|
|
| 32 |
assert result_resp.status_code == 200
|
| 33 |
assert result_resp.json()["final_score"] >= 0
|
| 34 |
|
| 35 |
+
def test_api_leaderboard(client):
|
|
|
|
| 36 |
# Submit a score
|
| 37 |
sub = {
|
| 38 |
"agent_name": "test_agent",
|
|
|
|
| 52 |
assert len(bug_entries) > 0
|
| 53 |
assert bug_entries[0]["agent_name"] == "test_agent"
|
| 54 |
|
| 55 |
+
def test_api_invalid_episode(client):
|
|
|
|
| 56 |
response = client.post("/step/nonexistent-id", json={
|
| 57 |
"action_type": "comment",
|
| 58 |
"body": "hello"
|