| # Deploying OpenSOC to Hugging Face Spaces |
|
|
| This is the one-time deployment recipe. The same Space serves both the |
| OpenEnv API (consumed by judge bots and `OpenSOCClient`) **and** a |
| Gradio "before vs after" UI at `/demo` for human reviewers. |
|
|
| ## 1. Local sanity check |
|
|
| ```bash |
| python -m venv .venv && source .venv/bin/activate |
| pip install -r requirements.txt |
| python server.py & |
| sleep 2 |
| curl -s http://localhost:7860/health | jq . |
| curl -s -X POST 'http://localhost:7860/reset?task=stage1_basic&mode=defender_only' | jq . |
| curl -s -I http://localhost:7860/demo | head -1 # should be 200 OK |
| kill %1 |
| ``` |
|
|
| ## 2. Build the Docker image locally |
|
|
| ```bash |
| docker build -t opensoc:latest . |
| docker run -p 7860:7860 opensoc:latest |
| # in another shell: |
| curl -s http://localhost:7860/tasks | jq . |
| open http://localhost:7860/demo |
| ``` |
|
|
| ## 3. Push to Hugging Face |
|
|
| The simplest path is via `huggingface-cli`; the second is a one-shot |
| script that does the same thing. |
|
|
| ### One-shot |
|
|
| ```bash |
| export HF_USER=<your-username> |
| huggingface-cli login # browser-based PAT login |
| bash scripts/deploy_to_hf.sh |
| ``` |
|
|
| ### Manual (equivalent) |
|
|
| ```bash |
| huggingface-cli login |
| huggingface-cli repo create opensoc-env --type space --space-sdk docker |
| # Use SPACE_README.md as the Space's README so HF picks up the docker SDK config: |
| cp SPACE_README.md /tmp/SPACE_README.md # save a copy |
| git checkout -b space-deploy |
| cp SPACE_README.md README.md # or prepend SPACE_README front-matter to README |
| git add README.md && git commit -m "Space metadata header" |
| git remote add space https://huggingface.co/spaces/$HF_USER/opensoc-env |
| git push space space-deploy:main |
| git checkout main && git checkout README.md |
| ``` |
|
|
| ## 4. Verify the deployed Space |
|
|
| ```bash |
| export OPENSOC_URL=https://<your-username>-opensoc-env.hf.space |
| python -c " |
| from client import OpenSOCClient |
| c = OpenSOCClient(base_url='$OPENSOC_URL') |
| print(c.health()) |
| print(c.tasks()) |
| obs = c.reset(task='stage1_basic', mode='defender_only', seed=1) |
| print('first log id:', obs['log_window'][0]['log_id']) |
| " |
| # And visually: |
| open $OPENSOC_URL/demo |
| ``` |
|
|
| `/demo` reads `data/demo_examples.json`. If you deployed before running |
| the GPU pipeline, the file holds the *placeholder* before-vs-after pairs |
| (always-dismiss vs verifier-oracle). Re-run `python -m eval.bake_demo` |
| on a GPU host (no `--placeholder`) and re-push to overwrite with real |
| trained-model outputs. |
|
|
| ## 5. (Optional) Run the eval harness against the live Space |
|
|
| ```bash |
| # Pure-CPU smoke run (no Unsloth required): |
| python -m eval.eval --smoke-only --holdout data/holdout.jsonl |
| ``` |
|
|
| ## OpenEnv hackathon checklist |
|
|
| - [x] `openenv.yaml` manifest with `endpoints.demo: GET /demo` |
| - [x] gym-style API: `reset` / `step` / `state` (+ `grade`, `tasks`, `health`) |
| - [x] non-reserved tool names (`craft_incident`, `submit_triage`) |
| - [x] FastAPI app exposed on port 7860 inside the container |
| - [x] Gradio UI mounted at `/demo` for the storytelling deliverable |
| - [x] Dockerfile suitable for Hugging Face Spaces (`sdk: docker`) |
| - [x] Client / server separation (`client/opensoc_client.py` is HTTP-only) |
| - [x] Frozen 200-incident eval set committed (`data/holdout.jsonl`) |
| - [x] 600-example SFT dataset committed (`data/sft_train.jsonl`) |
| - [x] 50 pre-baked demo pairs committed (`data/demo_examples.json`) |
| - [x] GRPO Colab/HF Jupyter notebook (`train_grpo.ipynb`) + one-shot |
| `scripts/run_full_pipeline.sh` |
| - [x] Pytest suite — 93 tests, all green |
| |