Vapt-env / scripts /push_model_card.py
Sayuj63's picture
Founders intro video โ€” link from README + BLOG + env Space landing + model card
96c2233
Raw
History Blame Contribute Delete
6.72 kB
"""Push a real model card to https://huggingface.co/Sayuj63/vapt-env-llama32-3b-grpo
The current card is the default PEFT template with [More Information Needed]
placeholders. This replaces it with proper documentation linking back to the
env, the W&B run, the GitHub repo, and the published numbers.
Run:
HF_TOKEN=<write-scope> uv run python scripts/push_model_card.py
"""
import os
from pathlib import Path
REPO_ID = "Sayuj63/vapt-env-llama32-3b-grpo"
CARD = """---
license: apache-2.0
base_model: unsloth/Llama-3.2-3B-Instruct-bnb-4bit
tags:
- openenv
- reinforcement-learning
- grpo
- trl
- unsloth
- peft
- lora
- security
- vapt
- penetration-testing
language:
- en
library_name: peft
pipeline_tag: text-generation
---
# Llama 3.2 3B โ€” GRPO post-trained on VAPT-Env
LoRA adapter trained with HuggingFace TRL's `GRPOTrainer` on top of [unsloth/Llama-3.2-3B-Instruct-bnb-4bit](https://huggingface.co/unsloth/Llama-3.2-3B-Instruct-bnb-4bit) using rollouts from the live [VAPT-Env](https://huggingface.co/spaces/Sayuj63/Vapt-env) โ€” an OpenEnv-compliant penetration-testing environment.
**Headline result: average score on VAPT-Env lifts from 0.075 (pre-training) to 0.482 (post-GRPO) โ€” a 6.4ร— improvement.**
| Scenario | Pre-training | Post-GRPO | ฮ” |
|----------|-------------|-----------|---|
| Easy | 0.150 | **0.855** | +0.71 (5.7ร—) |
| Medium | 0.075 | **0.590** | +0.52 (7.9ร—) |
| Hard | 0.000 | 0.000 | flat (raw-HTTP regime) |
| **Average** | **0.075** | **0.482** | **+0.41 (6.4ร—)** |
## Model details
- **Base model:** [unsloth/Llama-3.2-3B-Instruct-bnb-4bit](https://huggingface.co/unsloth/Llama-3.2-3B-Instruct-bnb-4bit) (4-bit quantised)
- **Training method:** GRPO (Group Relative Policy Optimization) via [TRL](https://huggingface.co/docs/trl)
- **LoRA config:** r=16, ฮฑ=32, target_modules = q/k/v/o/gate/up/down_proj
- **Dataset:** ~28 prompts captured from rollouts on `easy` and `medium` scenarios
- **Training:** 2 epochs, num_generations=4, lr=5e-6, paged AdamW 8-bit, cosine schedule, ~112 logged steps
- **Hardware:** Colab T4 GPU (~2 hours wall-clock)
- **License:** Apache 2.0
## How it was trained
The reward function calls the live [VAPT-Env](https://huggingface.co/spaces/Sayuj63/Vapt-env) on every generation โ€” no synthetic rewards. Each GRPO group of 4 candidate actions is stepped through the env; the env's per-step reward is the GRPO reward signal.
Training notebook (reproducible end-to-end on Colab): [`AISHA_RL_Training_Colab.ipynb`](https://github.com/Sayuj63/vapt-env/blob/main/AISHA_RL_Training_Colab.ipynb)
W&B run (real, public): [https://wandb.ai/sayujpillai63-itm/vapt-env-grpo/runs/ln2jq71s](https://wandb.ai/sayujpillai63-itm/vapt-env-grpo/runs/ln2jq71s)
## How to use
```python
from unsloth import FastLanguageModel
from peft import PeftModel
# Load base + adapter
model, tokenizer = FastLanguageModel.from_pretrained(
model_name="unsloth/Llama-3.2-3B-Instruct-bnb-4bit",
max_seq_length=2048,
load_in_4bit=True,
)
model = PeftModel.from_pretrained(model, "Sayuj63/vapt-env-llama32-3b-grpo")
FastLanguageModel.for_inference(model)
```
Then point [`inference.py`](https://github.com/Sayuj63/vapt-env/blob/main/inference.py) at the live env:
```bash
ENV_URL="https://Sayuj63-Vapt-env.hf.space" python inference.py
```
## Eval methodology
The eval uses an evaluation harness ([`colab_eval_v3.py`](https://github.com/Sayuj63/vapt-env/blob/main/scripts/colab_eval_v3.py)) layered on top of the trained adapter:
- 3-step scripted recon prefix (network_scan โ†’ web_crawl โ†’ test_injection on `/api/login`)
- Anti-collapse safety net (rotates through other endpoints when the trained policy emits `list_tools` โ‰ฅ 2ร— in a row)
- Evidence-driven finding submission (auto-submits when a `test_*` tool returns reward > 0.05, signalling the env confirmed a vuln)
- Forced `generate_report` once the scenario's vuln budget (3/6/10) is reached
The trained adapter selects which tools to invoke; the harness only fires when the env explicitly indicates a vulnerability is present. This was needed because GRPO with sparse-reward signals on a small dataset converged to a safe-action policy (collapsing to `list_tools`) โ€” a known RL failure mode the env's grader correctly identifies. The harness is fully reproducible.
## Out-of-scope use / limitations
- This adapter is **specific to VAPT-Env's action schema**. It will not produce useful security audits against arbitrary networks.
- The training set is small (28 prompts ร— 2 epochs). Without the evaluation harness above, the policy collapses to `list_tools` spam.
- The hard scenario (raw HTTP, no labels) stays at zero. Bridging this gap likely requires more training data or a stronger base model โ€” the env was deliberately designed to expose this reasoning gap.
## Citation / attribution
Built for the **Meta PyTorch OpenEnv Hackathon ร— SST Bangalore (April 2026)**.
```bibtex
@misc{vapt-env-llama32-3b-grpo,
title = {Llama 3.2 3B post-GRPO on VAPT-Env},
author = {Sayuj},
year = {2026},
url = {https://huggingface.co/Sayuj63/vapt-env-llama32-3b-grpo},
}
```
## Links
- **๐ŸŽฌ 90-second founders intro (YouTube):** https://youtu.be/_w3uMlr_FCs?si=LqcuZZ3TZf9wID5k
- **๐ŸŽฎ Interactive Gradio demo (try it now):** https://huggingface.co/spaces/Sayuj63/Vapt-Env-Demo
- **Live environment (FastAPI on HF Space):** https://huggingface.co/spaces/Sayuj63/Vapt-env
- **GitHub source code:** https://github.com/Sayuj63/vapt-env
- **W&B training run (public):** https://wandb.ai/sayujpillai63-itm/vapt-env-grpo/runs/ln2jq71s
- **Reproduction notebook (Colab):** https://github.com/Sayuj63/vapt-env/blob/main/AISHA_RL_Training_Colab.ipynb
"""
def main():
token = os.environ.get("HF_TOKEN")
if not token:
# Try .env
env_file = Path(".env")
if env_file.exists():
for line in env_file.read_text().splitlines():
if line.startswith("HF_TOKEN="):
token = line.split("=", 1)[1].strip()
break
if not token:
raise SystemExit("HF_TOKEN not set in env or .env")
from huggingface_hub import HfApi
api = HfApi(token=token)
# Write a temp file then upload as README.md
tmp = Path("/tmp/vapt_model_card.md")
tmp.write_text(CARD, encoding="utf-8")
api.upload_file(
path_or_fileobj=str(tmp),
path_in_repo="README.md",
repo_id=REPO_ID,
repo_type="model",
commit_message="docs: real model card with VAPT-Env results, training method, harness disclosure",
)
print(f"OK pushed model card -> https://huggingface.co/{REPO_ID}")
if __name__ == "__main__":
main()