Container-Port / README.md
Draken1606's picture
fix: emit strict score in END output and add scorer variance guard test
a8ffe4c
---
title: Container Port
colorFrom: gray
colorTo: blue
sdk: docker
pinned: false
license: mit
short_description: A simulation-based RL environment for container management
---
# Container Port Environment
An OpenEnv environment for container-yard stack planning at a shipping terminal.
## Task
Incoming containers have priority `1`, `2`, or `3`. The agent places each one into a bounded stack. During retrieval, every container sitting above the target counts as a rehandle and adds cost.
Goal: minimize total rehandles across the episode.
## Difficulty Levels
| Parameter | Easy | Medium | Hard |
|---|---|---|---|
| Stacks | 6 | 8 | 10 |
| Max height | 4 | 5 | 6 |
| Containers | 20 | 35 | 50 |
| Retrieval interval | 5 | 5 | 4 |
| Lookahead | 5 | 3 | 0 |
## Run Locally
```bash
pip install -e .
uvicorn server.app:app --host 0.0.0.0 --port 7860
```
Web UI: `http://127.0.0.1:7860/web`
Interactive dashboard with difficulty dropdown: `http://127.0.0.1:7860/dashboard`
For manual stateful checks, use the web endpoints:
```bash
curl http://127.0.0.1:7860/health
curl -X POST http://127.0.0.1:7860/web/reset -H "Content-Type: application/json" -d "{\"difficulty\":\"easy\"}"
curl -X POST http://127.0.0.1:7860/web/step -H "Content-Type: application/json" -d "{\"action\":{\"stack_index\":0}}"
```
`/reset` and `/step` are stateless simulation endpoints in `openenv-core 0.2.3`. For browser-style interactive testing, use `/web`, `/web/reset`, `/web/step`, or the WebSocket flow used by `inference.py`.
## Run Inference
```bash
python inference.py --difficulty all
python inference.py --difficulty easy
python inference.py --url http://127.0.0.1:7860 --difficulty all
```
LLM mode is enabled by default in `inference.py` and requires:
```bash
export API_BASE_URL="https://api.openai.com/v1" # or validator-provided proxy URL
export HF_TOKEN="your-validator-provided-token"
```
`MODEL_NAME` is optional and defaults to `meta-llama/Llama-3.1-8B-Instruct`.
`HF_TOKEN` is required by this script.
To run greedy mode locally without LLM calls:
```bash
python inference.py --no-llm
```
## Docker
```bash
docker build -t container-port-env .
docker run -p 7860:7860 container-port-env
```
## Tests
Run the full test suite:
```bash
pytest tests/test_openenv_env.py -v
```
| Test | What it covers |
|---|---|
| test_reset_returns_valid_obs | Reset returns correct stack count, step=0, no rehandles |
| test_step_valid_action | Valid placement increments step and fills stack |
| test_step_invalid_action_penalized | Out-of-range stack index returns -2.0 reward |
| test_score_in_range | Full episode score stays in [0.0, 1.0] |
| test_full_episode_completes | All 3 difficulties reach done=True within 500 steps |
| test_lookahead_visibility | Easy shows more upcoming retrievals than hard (hard=0) |
| test_reward_is_dense | At least 50% of steps have non-zero reward |
| test_no_double_retrieval | retrieval_pointer never exceeds queue length |
| test_health_route | GET /health returns 200 |
| test_web_ui_route | GET /web returns 200 (Gradio UI) |
| test_http_reset_returns_observation | POST /reset returns valid easy-mode observation |
| test_http_reset_then_step_preserves_state | Sequential reset+step operates on same episode |