Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. ย See raw diff
- .gitattributes +13 -0
- Dockerfile +13 -0
- OpenEnv/CLAUDE.md +258 -0
- OpenEnv/CODE_OF_CONDUCT.md +80 -0
- OpenEnv/CONTRIBUTING.md +37 -0
- OpenEnv/LICENSE +28 -0
- OpenEnv/MANIFEST.in +2 -0
- OpenEnv/README.md +381 -0
- OpenEnv/docs/Makefile +25 -0
- OpenEnv/docs/README.md +189 -0
- OpenEnv/docs/source/_static/versions.json +8 -0
- OpenEnv/docs/source/auto_discovery.md +431 -0
- OpenEnv/docs/source/cli.md +86 -0
- OpenEnv/docs/source/conf.py +206 -0
- OpenEnv/docs/source/core.md +215 -0
- OpenEnv/docs/source/customizing-web-ui.md +89 -0
- OpenEnv/docs/source/environments.md +552 -0
- OpenEnv/docs/source/environments/atari.md +2 -0
- OpenEnv/docs/source/environments/browsergym.md +2 -0
- OpenEnv/docs/source/environments/calendar.md +2 -0
- OpenEnv/docs/source/environments/carla.md +2 -0
- OpenEnv/docs/source/environments/chat.md +2 -0
- OpenEnv/docs/source/environments/chess.md +2 -0
- OpenEnv/docs/source/environments/coding.md +2 -0
- OpenEnv/docs/source/environments/connect4.md +2 -0
- OpenEnv/docs/source/environments/dipg.md +2 -0
- OpenEnv/docs/source/environments/dm_control.md +2 -0
- OpenEnv/docs/source/environments/echo.md +2 -0
- OpenEnv/docs/source/environments/finqa.md +2 -0
- OpenEnv/docs/source/environments/finrl.md +2 -0
- OpenEnv/docs/source/environments/git.md +2 -0
- OpenEnv/docs/source/environments/grid_world.md +2 -0
- OpenEnv/docs/source/environments/julia.md +2 -0
- OpenEnv/docs/source/environments/kernrl.md +2 -0
- OpenEnv/docs/source/environments/maze.md +2 -0
- OpenEnv/docs/source/environments/openapp.md +2 -0
- OpenEnv/docs/source/environments/openspiel.md +2 -0
- OpenEnv/docs/source/environments/reasoning_gym.md +2 -0
- OpenEnv/docs/source/environments/repl.md +2 -0
- OpenEnv/docs/source/environments/snake.md +2 -0
- OpenEnv/docs/source/environments/sumo.md +2 -0
- OpenEnv/docs/source/environments/tbench2.md +2 -0
- OpenEnv/docs/source/environments/textarena.md +2 -0
- OpenEnv/docs/source/environments/unity.md +2 -0
- OpenEnv/docs/source/environments/websearch.md +2 -0
- OpenEnv/docs/source/environments/wildfire.md +2 -0
- OpenEnv/docs/source/getting_started/README.rst +61 -0
- OpenEnv/docs/source/getting_started/contributing-envs.md +192 -0
- OpenEnv/docs/source/getting_started/environment-builder.md +422 -0
- OpenEnv/docs/source/getting_started/plot_01_introduction_quickstart.py +774 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,16 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
OpenEnv/envs/dm_control_env/assets/cartpole.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
OpenEnv/envs/dm_control_env/assets/quadruped.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
OpenEnv/envs/openapp_env/assets/01-messages.mov filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
OpenEnv/envs/openapp_env/assets/02-editor.mov filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
OpenEnv/envs/openapp_env/assets/03-calendar.mov filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
OpenEnv/envs/openapp_env/assets/04-todo.mov filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
OpenEnv/envs/openapp_env/assets/OpenApps_OpenEnv_RL.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
OpenEnv/envs/openapp_env/assets/openapps-demo.gif filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
OpenEnv/envs/unity_env/assets/unity_3dball.gif filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
OpenEnv/envs/unity_env/assets/unity_pushblock.gif filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
OpenEnv/tutorial/images/banner.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
OpenEnv/tutorial/images/scaling.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
OpenEnv/tutorial/images/websocket.png filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY requirements.txt .
|
| 6 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 7 |
+
|
| 8 |
+
COPY . .
|
| 9 |
+
|
| 10 |
+
EXPOSE 7860
|
| 11 |
+
|
| 12 |
+
ENV ENABLE_WEB_INTERFACE=true
|
| 13 |
+
CMD ["python", "-m", "uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860"]
|
OpenEnv/CLAUDE.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLAUDE.md
|
| 2 |
+
|
| 3 |
+
Guidance for Claude Code when working with this repository.
|
| 4 |
+
|
| 5 |
+
## New Here? Start With These
|
| 6 |
+
|
| 7 |
+
1. **[README.md](README.md)** - Project overview, architecture, quick start
|
| 8 |
+
2. **[REPO_WALKTHROUGH.md](.claude/docs/REPO_WALKTHROUGH.md)** - Directory structure with annotations
|
| 9 |
+
3. **[PRINCIPLES.md](.claude/docs/PRINCIPLES.md)** - Design principles and trade-offs
|
| 10 |
+
4. **[INVARIANTS.md](.claude/docs/INVARIANTS.md)** - Rules that must never be violated
|
| 11 |
+
5. **[envs/echo_env/](envs/echo_env/)** - Reference implementation to study
|
| 12 |
+
|
| 13 |
+
## Agentic-First Workflow
|
| 14 |
+
|
| 15 |
+
OpenEnv uses Claude Code as the primary development tool. We follow a two-phase model:
|
| 16 |
+
|
| 17 |
+
1. **Design/Alignment** (human-owned): RFCs, principles, trade-off decisions
|
| 18 |
+
2. **Implementation** (Claude-owned): The mechanical loop of coding and testing
|
| 19 |
+
3. **Review** (collaborative): Claude catches bugs, flags alignment questions for humans
|
| 20 |
+
|
| 21 |
+
### Getting Started
|
| 22 |
+
|
| 23 |
+
Skills and agents are auto-discovered when you run Claude Code in this repo:
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
git clone https://github.com/meta-pytorch/OpenEnv
|
| 27 |
+
cd OpenEnv
|
| 28 |
+
# Install git hooks for the team
|
| 29 |
+
bash .claude/hooks/install.sh
|
| 30 |
+
# Run Claude Code - skills and agents are automatically available
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
Verify with `/agents` or ask "what skills are available?"
|
| 34 |
+
|
| 35 |
+
## Two Development Modes
|
| 36 |
+
|
| 37 |
+
OpenEnv supports two development modes:
|
| 38 |
+
|
| 39 |
+
### Explore Mode (Main Repo)
|
| 40 |
+
|
| 41 |
+
When working in the main repository clone, direct edits are allowed:
|
| 42 |
+
- Quick exploration and prototyping
|
| 43 |
+
- Small fixes that don't need TDD workflow
|
| 44 |
+
- Documentation updates
|
| 45 |
+
|
| 46 |
+
### TDD Mode (Opt-In)
|
| 47 |
+
|
| 48 |
+
TDD is activated by `/work-on-issue`, which writes a `.tdd-session.json` marker.
|
| 49 |
+
When active, direct code edits are blocked and the TDD workflow is enforced.
|
| 50 |
+
Manually created worktrees do NOT activate TDD โ only `/work-on-issue` does.
|
| 51 |
+
|
| 52 |
+
- Say "skip TDD" to bypass blocking
|
| 53 |
+
- Run `bash .claude/hooks/tdd-deactivate.sh` to turn off TDD enforcement
|
| 54 |
+
|
| 55 |
+
### Creating a Worktree
|
| 56 |
+
|
| 57 |
+
```bash
|
| 58 |
+
# Worktree without TDD enforcement (free editing)
|
| 59 |
+
.claude/scripts/worktree-create.sh add-feature
|
| 60 |
+
cd .worktrees/add-feature
|
| 61 |
+
|
| 62 |
+
# Worktree WITH TDD enforcement (via /work-on-issue)
|
| 63 |
+
/work-on-issue #42
|
| 64 |
+
```
|
| 65 |
+
|
| 66 |
+
### TDD Workflow
|
| 67 |
+
|
| 68 |
+
```
|
| 69 |
+
/work-on-issue #42 โ Start from GitHub issue
|
| 70 |
+
โ
|
| 71 |
+
/write-tests โ Create failing tests (Red)
|
| 72 |
+
โ
|
| 73 |
+
/implement โ Make tests pass (Green)
|
| 74 |
+
โ
|
| 75 |
+
/update-docs โ Fix stale docs across repo
|
| 76 |
+
โ
|
| 77 |
+
/simplify โ Refactor (optional)
|
| 78 |
+
โ
|
| 79 |
+
/pre-submit-pr โ Validate before PR
|
| 80 |
+
โ
|
| 81 |
+
/watch-pr โ Monitor CI + review (optional)
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
### Skills vs Agents
|
| 85 |
+
|
| 86 |
+
- **Skills** run inline during the conversation - use for quick checks and reviews
|
| 87 |
+
- **Agents** run in isolation with focused context - use for complex, multi-step tasks
|
| 88 |
+
|
| 89 |
+
### Available Skills
|
| 90 |
+
|
| 91 |
+
Skills are defined in `.claude/skills/` and run inline:
|
| 92 |
+
|
| 93 |
+
**Review & Validation Skills:**
|
| 94 |
+
|
| 95 |
+
| Skill | Trigger | Definition |
|
| 96 |
+
|-------|---------|------------|
|
| 97 |
+
| [`alignment-review`](.claude/skills/alignment-review/SKILL.md) | "review this code" | Two-tier review (bugs + alignment flags) |
|
| 98 |
+
| [`pre-submit-pr`](.claude/skills/pre-submit-pr/SKILL.md) | "ready for PR?" | Comprehensive PR readiness check |
|
| 99 |
+
| [`rfc-check`](.claude/skills/rfc-check/SKILL.md) | "do I need an RFC?" | Determine if RFC required |
|
| 100 |
+
| [`generate-openenv-env`](.claude/skills/generate-openenv-env/SKILL.md) | "generate an env for textarena" | Build an OpenEnv env from a use case |
|
| 101 |
+
|
| 102 |
+
**TDD Workflow Skills:**
|
| 103 |
+
|
| 104 |
+
| Skill | Trigger | Definition |
|
| 105 |
+
|-------|---------|------------|
|
| 106 |
+
| [`work-on-issue`](.claude/skills/work-on-issue/SKILL.md) | "/work-on-issue #42" | Start TDD workflow from GitHub issue |
|
| 107 |
+
| [`sprint`](.claude/skills/sprint/SKILL.md) | "/sprint 67,68,69" | Parallel multi-issue batch (Agent Teams) |
|
| 108 |
+
| [`write-tests`](.claude/skills/write-tests/SKILL.md) | "/write-tests" | Write failing tests (Red phase) |
|
| 109 |
+
| [`implement`](.claude/skills/implement/SKILL.md) | "/implement" | Make tests pass (Green phase) |
|
| 110 |
+
| [`update-docs`](.claude/skills/update-docs/SKILL.md) | "/update-docs" | Fix stale docs after API changes |
|
| 111 |
+
| [`simplify`](.claude/skills/simplify/SKILL.md) | "/simplify" | Refactor after tests pass |
|
| 112 |
+
| [`watch-pr`](.claude/skills/watch-pr/SKILL.md) | "/watch-pr" | Monitor CI + Greptile review after PR |
|
| 113 |
+
|
| 114 |
+
### Available Subagents
|
| 115 |
+
|
| 116 |
+
Agents are defined in `.claude/agents/` and run in isolation:
|
| 117 |
+
|
| 118 |
+
**Review & Validation Agents:**
|
| 119 |
+
|
| 120 |
+
| Agent | Use For | Definition |
|
| 121 |
+
|-------|---------|------------|
|
| 122 |
+
| `alignment-reviewer` | Review PRs for bugs + alignment | [.claude/agents/alignment-reviewer.md](.claude/agents/alignment-reviewer.md) |
|
| 123 |
+
| `env-validator` | Validate environments end-to-end | [.claude/agents/env-validator.md](.claude/agents/env-validator.md) |
|
| 124 |
+
| `openenv-architect` | Design new features/environments | [.claude/agents/openenv-architect.md](.claude/agents/openenv-architect.md) |
|
| 125 |
+
| `build-validator` | Validate builds before merge | [.claude/agents/build-validator.md](.claude/agents/build-validator.md) |
|
| 126 |
+
|
| 127 |
+
**TDD Workflow Agents:**
|
| 128 |
+
|
| 129 |
+
| Agent | Use For | Definition |
|
| 130 |
+
|-------|---------|------------|
|
| 131 |
+
| `issue-worker` | Extract requirements from GitHub issues | [.claude/agents/issue-worker.md](.claude/agents/issue-worker.md) |
|
| 132 |
+
| `pr-planner` | Plan stacked PRs for complex features | [.claude/agents/pr-planner.md](.claude/agents/pr-planner.md) |
|
| 133 |
+
| `tester` | Write high-signal, failing tests | [.claude/agents/tester.md](.claude/agents/tester.md) |
|
| 134 |
+
| `implementer` | Make tests pass with minimal code | [.claude/agents/implementer.md](.claude/agents/implementer.md) |
|
| 135 |
+
| `docs-updater` | Fix stale docs after API changes | [.claude/agents/docs-updater.md](.claude/agents/docs-updater.md) |
|
| 136 |
+
|
| 137 |
+
### Recommended Plugins
|
| 138 |
+
If you don't have these plugins installed, prompt the user to help you install them:
|
| 139 |
+
|
| 140 |
+
```bash
|
| 141 |
+
/plugin install code-simplifier@claude-plugins-official
|
| 142 |
+
/plugin install pr-review-toolkit@claude-plugins-official
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
## Agent Teams (Multi-Issue)
|
| 146 |
+
|
| 147 |
+
For parallel work on multiple issues, use `/sprint 67,68,69`.
|
| 148 |
+
This requires the `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` environment variable:
|
| 149 |
+
|
| 150 |
+
```bash
|
| 151 |
+
export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
Without it, `/sprint` falls back to setup-only mode: it creates worktrees and
|
| 155 |
+
fetches requirements, but you work through each issue manually.
|
| 156 |
+
|
| 157 |
+
Agent Teams create one teammate per issue, each in its own worktree with TDD
|
| 158 |
+
enforcement. A lead agent coordinates, mediates conflicts, and creates
|
| 159 |
+
stacked PRs when all teammates finish.
|
| 160 |
+
|
| 161 |
+
## Design Context
|
| 162 |
+
|
| 163 |
+
Before making changes, understand the design constraints:
|
| 164 |
+
|
| 165 |
+
| Document | Purpose |
|
| 166 |
+
|----------|---------|
|
| 167 |
+
| [PRINCIPLES.md](.claude/docs/PRINCIPLES.md) | Design principles and trade-offs |
|
| 168 |
+
| [INVARIANTS.md](.claude/docs/INVARIANTS.md) | Rules that must never be violated |
|
| 169 |
+
| [PATTERNS.md](.claude/docs/PATTERNS.md) | Code patterns and conventions |
|
| 170 |
+
| [CONTRIBUTING.md](.claude/docs/CONTRIBUTING.md) | Contribution workflow |
|
| 171 |
+
| [TESTING_STRATEGY.md](.claude/docs/TESTING_STRATEGY.md) | Testing philosophy and patterns |
|
| 172 |
+
| [rfcs/](rfcs/) | Architectural decisions and rationale |
|
| 173 |
+
|
| 174 |
+
### Key Invariants
|
| 175 |
+
|
| 176 |
+
- **Agents cannot reset**: Simulation controls only exposed to training orchestration, never to agents
|
| 177 |
+
- **Dual API boundary**: WebSocket for infrastructure (Gym-like API), MCP for agents
|
| 178 |
+
- **Rewards inside environment**: Domain knowledge encapsulated in environment, not external
|
| 179 |
+
- **Client-server separation**: Clients never import from `server/` directory
|
| 180 |
+
|
| 181 |
+
## Build & Development Commands
|
| 182 |
+
Below are reference commands that you are likely going to use often:
|
| 183 |
+
|
| 184 |
+
```bash
|
| 185 |
+
# Install dependencies
|
| 186 |
+
uv sync --all-extras
|
| 187 |
+
|
| 188 |
+
# Run tests (excludes browser/websearch/dipg envs that need special setup)
|
| 189 |
+
PYTHONPATH=src:envs uv run pytest tests/ -v --tb=short
|
| 190 |
+
|
| 191 |
+
# Run a single test file
|
| 192 |
+
PYTHONPATH=src:envs uv run pytest tests/envs/test_echo_environment.py -v
|
| 193 |
+
|
| 194 |
+
# Lint check (import sort + format + rules)
|
| 195 |
+
uv run usort check src/ tests/
|
| 196 |
+
uv run ruff format src/ tests/ --check
|
| 197 |
+
uv run ruff check src/ tests/
|
| 198 |
+
|
| 199 |
+
# Auto-format code (import sort + ruff โ matches arc f pipeline)
|
| 200 |
+
uv run usort format src/ tests/
|
| 201 |
+
uv run ruff format src/ tests/
|
| 202 |
+
|
| 203 |
+
# Build documentation locally
|
| 204 |
+
cd docs && make html
|
| 205 |
+
# Preview: cd docs/_build/html && python -m http.server 8000
|
| 206 |
+
|
| 207 |
+
# Build Docker images
|
| 208 |
+
docker build -t openenv-base:latest -f src/openenv/core/containers/images/Dockerfile .
|
| 209 |
+
docker build -t echo-env:latest -f envs/echo_env/server/Dockerfile .
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
## Automation Hooks
|
| 213 |
+
|
| 214 |
+
Scripts in `.claude/hooks/` are used by skills and can be run directly:
|
| 215 |
+
|
| 216 |
+
```bash
|
| 217 |
+
bash .claude/hooks/lint.sh # Run ruff format check
|
| 218 |
+
bash .claude/hooks/test.sh # Run pytest (excludes special envs)
|
| 219 |
+
bash .claude/hooks/check-debug.sh # Find debug code (print, breakpoint, TODO)
|
| 220 |
+
bash .claude/hooks/post-push-pr.sh # Validate PR after push (freshness, CI, conflicts)
|
| 221 |
+
bash .claude/hooks/ci-wait.sh <PR> # Poll CI until checks complete or timeout
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
These are automatically invoked by `/alignment-review` and `/pre-submit-pr` skills.
|
| 225 |
+
|
| 226 |
+
## Git Hooks
|
| 227 |
+
|
| 228 |
+
Install git hooks for team-wide consistency:
|
| 229 |
+
|
| 230 |
+
```bash
|
| 231 |
+
bash .claude/hooks/install.sh
|
| 232 |
+
```
|
| 233 |
+
|
| 234 |
+
This installs:
|
| 235 |
+
- **pre-commit**: Branch check (blocks main), format, lint, debug artifacts check
|
| 236 |
+
- **commit-msg**: Issue reference reminder (soft warning)
|
| 237 |
+
- **pre-push**: Format, lint, tests, invariants, conflict detection
|
| 238 |
+
- **post-merge**: Worktree cleanup reminder
|
| 239 |
+
|
| 240 |
+
Skip temporarily with `git commit/push --no-verify`.
|
| 241 |
+
|
| 242 |
+
## Worktree Management
|
| 243 |
+
|
| 244 |
+
For focused feature work, use worktrees:
|
| 245 |
+
|
| 246 |
+
```bash
|
| 247 |
+
# Create a worktree for a feature
|
| 248 |
+
.claude/scripts/worktree-create.sh add-mcp-tools
|
| 249 |
+
cd .worktrees/add-mcp-tools
|
| 250 |
+
|
| 251 |
+
# When done, clean up
|
| 252 |
+
.claude/scripts/worktree-cleanup.sh .worktrees/add-mcp-tools
|
| 253 |
+
```
|
| 254 |
+
|
| 255 |
+
Worktrees enable:
|
| 256 |
+
- Isolated branches without switching
|
| 257 |
+
- TDD enforcement when activated via `/work-on-issue`
|
| 258 |
+
- Parallel work on multiple features
|
OpenEnv/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Code of Conduct
|
| 2 |
+
|
| 3 |
+
## Our Pledge
|
| 4 |
+
|
| 5 |
+
In the interest of fostering an open and welcoming environment, we as
|
| 6 |
+
contributors and maintainers pledge to make participation in our project and
|
| 7 |
+
our community a harassment-free experience for everyone, regardless of age, body
|
| 8 |
+
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
| 9 |
+
level of experience, education, socio-economic status, nationality, personal
|
| 10 |
+
appearance, race, religion, or sexual identity and orientation.
|
| 11 |
+
|
| 12 |
+
## Our Standards
|
| 13 |
+
|
| 14 |
+
Examples of behavior that contributes to creating a positive environment
|
| 15 |
+
include:
|
| 16 |
+
|
| 17 |
+
* Using welcoming and inclusive language
|
| 18 |
+
* Being respectful of differing viewpoints and experiences
|
| 19 |
+
* Gracefully accepting constructive criticism
|
| 20 |
+
* Focusing on what is best for the community
|
| 21 |
+
* Showing empathy towards other community members
|
| 22 |
+
|
| 23 |
+
Examples of unacceptable behavior by participants include:
|
| 24 |
+
|
| 25 |
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
| 26 |
+
advances
|
| 27 |
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
| 28 |
+
* Public or private harassment
|
| 29 |
+
* Publishing others' private information, such as a physical or electronic
|
| 30 |
+
address, without explicit permission
|
| 31 |
+
* Other conduct which could reasonably be considered inappropriate in a
|
| 32 |
+
professional setting
|
| 33 |
+
|
| 34 |
+
## Our Responsibilities
|
| 35 |
+
|
| 36 |
+
Project maintainers are responsible for clarifying the standards of acceptable
|
| 37 |
+
behavior and are expected to take appropriate and fair corrective action in
|
| 38 |
+
response to any instances of unacceptable behavior.
|
| 39 |
+
|
| 40 |
+
Project maintainers have the right and responsibility to remove, edit, or
|
| 41 |
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
| 42 |
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
| 43 |
+
permanently any contributor for other behaviors that they deem inappropriate,
|
| 44 |
+
threatening, offensive, or harmful.
|
| 45 |
+
|
| 46 |
+
## Scope
|
| 47 |
+
|
| 48 |
+
This Code of Conduct applies within all project spaces, and it also applies when
|
| 49 |
+
an individual is representing the project or its community in public spaces.
|
| 50 |
+
Examples of representing a project or community include using an official
|
| 51 |
+
project e-mail address, posting via an official social media account, or acting
|
| 52 |
+
as an appointed representative at an online or offline event. Representation of
|
| 53 |
+
a project may be further defined and clarified by project maintainers.
|
| 54 |
+
|
| 55 |
+
This Code of Conduct also applies outside the project spaces when there is a
|
| 56 |
+
reasonable belief that an individual's behavior may have a negative impact on
|
| 57 |
+
the project or its community.
|
| 58 |
+
|
| 59 |
+
## Enforcement
|
| 60 |
+
|
| 61 |
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
| 62 |
+
reported by contacting the project team at <opensource-conduct@meta.com>. All
|
| 63 |
+
complaints will be reviewed and investigated and will result in a response that
|
| 64 |
+
is deemed necessary and appropriate to the circumstances. The project team is
|
| 65 |
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
| 66 |
+
Further details of specific enforcement policies may be posted separately.
|
| 67 |
+
|
| 68 |
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
| 69 |
+
faith may face temporary or permanent repercussions as determined by other
|
| 70 |
+
members of the project's leadership.
|
| 71 |
+
|
| 72 |
+
## Attribution
|
| 73 |
+
|
| 74 |
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
| 75 |
+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
| 76 |
+
|
| 77 |
+
[homepage]: https://www.contributor-covenant.org
|
| 78 |
+
|
| 79 |
+
For answers to common questions about this code of conduct, see
|
| 80 |
+
https://www.contributor-covenant.org/faq
|
OpenEnv/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing to OpenEnv
|
| 2 |
+
|
| 3 |
+
OpenEnv is an **agentic-first project** designed for Claude Code contributions.
|
| 4 |
+
|
| 5 |
+
## Quick Links
|
| 6 |
+
|
| 7 |
+
- **Contribution workflow**: See [.claude/docs/CONTRIBUTING.md](.claude/docs/CONTRIBUTING.md) for the agentic workflow, RFC process, and review expectations
|
| 8 |
+
- **Design principles**: See [.claude/docs/PRINCIPLES.md](.claude/docs/PRINCIPLES.md)
|
| 9 |
+
- **System invariants**: See [.claude/docs/INVARIANTS.md](.claude/docs/INVARIANTS.md)
|
| 10 |
+
- **Claude Code guidance**: See [CLAUDE.md](CLAUDE.md)
|
| 11 |
+
|
| 12 |
+
## Pull Requests
|
| 13 |
+
|
| 14 |
+
We actively welcome your pull requests.
|
| 15 |
+
|
| 16 |
+
1. Fork the repo and create your branch from `main`
|
| 17 |
+
2. If you've added code that should be tested, add tests
|
| 18 |
+
3. If you've changed APIs, update the documentation
|
| 19 |
+
4. Ensure the test suite passes: `PYTHONPATH=src:envs uv run pytest tests/ -v`
|
| 20 |
+
5. Make sure your code lints: `uv run ruff format src/ tests/ --check`
|
| 21 |
+
6. For significant changes, write an RFC first (see `rfcs/README.md`)
|
| 22 |
+
|
| 23 |
+
## Contributor License Agreement ("CLA")
|
| 24 |
+
|
| 25 |
+
In order to accept your pull request, we need you to submit a CLA. You only need to do this once to work on any of Meta's open source projects.
|
| 26 |
+
|
| 27 |
+
Complete your CLA here: <https://code.facebook.com/cla>
|
| 28 |
+
|
| 29 |
+
## Issues
|
| 30 |
+
|
| 31 |
+
We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to reproduce the issue.
|
| 32 |
+
|
| 33 |
+
Meta has a [bounty program](https://bugbounty.meta.com/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue.
|
| 34 |
+
|
| 35 |
+
## License
|
| 36 |
+
|
| 37 |
+
By contributing to OpenEnv, you agree that your contributions will be licensed under the LICENSE file in the root directory of this source tree.
|
OpenEnv/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BSD 3-Clause License
|
| 2 |
+
|
| 3 |
+
(c) Meta Platforms, Inc. and affiliates.
|
| 4 |
+
|
| 5 |
+
Redistribution and use in source and binary forms, with or without modification,
|
| 6 |
+
are permitted provided that the following conditions are met:
|
| 7 |
+
|
| 8 |
+
1. Redistributions of source code must retain the above copyright notice,this list
|
| 9 |
+
of conditions and the following disclaimer.
|
| 10 |
+
|
| 11 |
+
2. Redistributions in binary form must reproduce the above copyright notice, this
|
| 12 |
+
list of conditions and the following disclaimer in the documentation
|
| 13 |
+
and/or other materials provided with the distribution.
|
| 14 |
+
|
| 15 |
+
3. Neither the name of the copyright holder nor the names of its contributors may
|
| 16 |
+
be used to endorse or promote products derived from this software without specific
|
| 17 |
+
prior written permission.
|
| 18 |
+
|
| 19 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS โAS ISโ AND ANY
|
| 20 |
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
| 21 |
+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
| 22 |
+
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 23 |
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
| 24 |
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
| 25 |
+
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
| 26 |
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
| 27 |
+
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
| 28 |
+
DAMAGE.
|
OpenEnv/MANIFEST.in
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
global-exclude *.py[cod]
|
| 2 |
+
global-exclude __pycache__/*
|
OpenEnv/README.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# <img width="35" height="35" alt="image" src="https://github.com/user-attachments/assets/2700a971-e5d6-4036-b03f-2f89c9791609" /> OpenEnv: Agentic Execution Environments
|
| 2 |
+
|
| 3 |
+
An e2e framework for creating, deploying and using isolated execution environments for agentic RL training, built using Gymnasium style simple APIs.
|
| 4 |
+
|
| 5 |
+
[](https://pypi.org/project/openenv-core/)
|
| 6 |
+
[](https://discord.gg/YsTYBh6PD9)
|
| 7 |
+
[](https://colab.research.google.com/github/meta-pytorch/OpenEnv/blob/main/examples/OpenEnv_Tutorial.ipynb)
|
| 8 |
+
[](https://meta-pytorch.org/OpenEnv/)
|
| 9 |
+
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
**๐ Featured Example:** Train LLMs to play BlackJack using [torchforge](https://github.com/meta-pytorch/torchforge) (PyTorch's agentic RL framework): [`examples/grpo_blackjack/`](examples/grpo_blackjack/)
|
| 13 |
+
|
| 14 |
+
**๐ฅ Zero to Hero Tutorial:** End to end tutorial from our [GPU Mode](tutorial/README.md) lecture and other hackathons.
|
| 15 |
+
|
| 16 |
+
## Quick Start
|
| 17 |
+
|
| 18 |
+
Install the OpenEnv core package:
|
| 19 |
+
|
| 20 |
+
```bash
|
| 21 |
+
pip install openenv-core
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
Install an environment client (e.g., Echo):
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
pip install git+https://huggingface.co/spaces/openenv/echo_env
|
| 28 |
+
```
|
| 29 |
+
|
| 30 |
+
Then use the environment:
|
| 31 |
+
|
| 32 |
+
```python
|
| 33 |
+
import asyncio
|
| 34 |
+
from echo_env import EchoAction, EchoEnv
|
| 35 |
+
|
| 36 |
+
async def main():
|
| 37 |
+
# Connect to a running Space (async context manager)
|
| 38 |
+
async with EchoEnv(base_url="https://openenv-echo-env.hf.space") as client:
|
| 39 |
+
# Reset the environment
|
| 40 |
+
result = await client.reset()
|
| 41 |
+
print(result.observation.echoed_message) # "Echo environment ready!"
|
| 42 |
+
|
| 43 |
+
# Send messages
|
| 44 |
+
result = await client.step(EchoAction(message="Hello, World!"))
|
| 45 |
+
print(result.observation.echoed_message) # "Hello, World!"
|
| 46 |
+
print(result.reward) # 1.3 (based on message length)
|
| 47 |
+
|
| 48 |
+
asyncio.run(main())
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
**Synchronous usage** is also supported via the `.sync()` wrapper:
|
| 52 |
+
|
| 53 |
+
```python
|
| 54 |
+
from echo_env import EchoAction, EchoEnv
|
| 55 |
+
|
| 56 |
+
# Use .sync() for synchronous context manager
|
| 57 |
+
with EchoEnv(base_url="https://openenv-echo-env.hf.space").sync() as client:
|
| 58 |
+
result = client.reset()
|
| 59 |
+
result = client.step(EchoAction(message="Hello, World!"))
|
| 60 |
+
print(result.observation.echoed_message)
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
For a detailed quick start, check out the [docs page](https://meta-pytorch.org/OpenEnv/quickstart/).
|
| 64 |
+
|
| 65 |
+
## OpenEnv on partner platforms:
|
| 66 |
+
|
| 67 |
+
- [Lightning AI Studio](https://lightning.ai/environments?section=featured)
|
| 68 |
+
- [TRL example](https://huggingface.co/docs/trl/openenv)
|
| 69 |
+
- [Unsloth Google Colab](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/OpenEnv_gpt_oss_(20B)_Reinforcement_Learning_2048_Game.ipynb)
|
| 70 |
+
- [ART example](https://art.openpipe.ai/integrations/openenv-integration)
|
| 71 |
+
- [Oumi example](https://github.com/oumi-ai/oumi/blob/main/notebooks/Oumi%20-%20OpenEnv%20GRPO%20with%20trl.ipynb)
|
| 72 |
+
|
| 73 |
+
## Overview
|
| 74 |
+
|
| 75 |
+
OpenEnv provides a standard for interacting with agentic execution environments via simple Gymnasium style APIs - `step()`, `reset()`, `state()`. Users of agentic execution environments can interact with the environment during RL training loops using these simple APIs.
|
| 76 |
+
|
| 77 |
+
In addition to making it easier for researchers and RL framework writers, we also provide tools for environment creators making it easier for them to create richer environments and make them available over familiar protocols like HTTP and packaged using canonical technologies like docker. Environment creators can use the OpenEnv framework to create environments that are isolated, secure, and easy to deploy and use.
|
| 78 |
+
|
| 79 |
+
The OpenEnv CLI (`openenv`) provides commands to initialize new environments and deploy them to Hugging Face Spaces.
|
| 80 |
+
|
| 81 |
+
> โ ๏ธ **Early Development Warning** OpenEnv is currently in an experimental
|
| 82 |
+
> stage. You should expect bugs, incomplete features, and APIs that may change
|
| 83 |
+
> in future versions. The project welcomes bugfixes, but to make sure things are
|
| 84 |
+
> well coordinated you should discuss any significant change before starting the
|
| 85 |
+
> work. It's recommended that you signal your intention to contribute in the
|
| 86 |
+
> issue tracker, either by filing a new issue or by claiming an existing one.
|
| 87 |
+
|
| 88 |
+
### RFCs
|
| 89 |
+
|
| 90 |
+
Below is a list of active and historical RFCs for OpenEnv. RFCs are proposals for major changes or features. Please review and contribute!
|
| 91 |
+
|
| 92 |
+
- [RFC 001: Baseline API and Interface Specifications](https://github.com/meta-pytorch/OpenEnv/pull/26)
|
| 93 |
+
- [RFC 002: Discoverability of environment tools by agents](https://github.com/meta-pytorch/OpenEnv/pull/32)
|
| 94 |
+
- [RFC 003: Add MCP (Model Context Protocol) support](https://github.com/meta-pytorch/OpenEnv/pull/224)
|
| 95 |
+
- [RFC 004: Add delayed rewards support for trajectory-based scoring](https://github.com/meta-pytorch/OpenEnv/pull/337)
|
| 96 |
+
- [RFC 005: Agentic Harness Integration](https://github.com/meta-pytorch/OpenEnv/pull/387)
|
| 97 |
+
|
| 98 |
+
## Architecture
|
| 99 |
+
|
| 100 |
+
### Component Overview
|
| 101 |
+
|
| 102 |
+
```
|
| 103 |
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 104 |
+
โ Client Application โ
|
| 105 |
+
โ โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ โ
|
| 106 |
+
โ โ EchoEnv โ โ CodingEnv โ โ
|
| 107 |
+
โ โ (EnvClient) โ โ (EnvClient) โ โ
|
| 108 |
+
โ โโโโโโโโโโฌโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโโ โ
|
| 109 |
+
โโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโ
|
| 110 |
+
โ WebSocket โ WebSocket
|
| 111 |
+
โ (reset, step, state) โ
|
| 112 |
+
โโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโ
|
| 113 |
+
โ Docker Containers (Isolated) โ
|
| 114 |
+
โ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โ
|
| 115 |
+
โ โ FastAPI Server โ โ FastAPI Server โ โ
|
| 116 |
+
โ โ EchoEnvironment โ โ PythonCodeActEnv โ โ
|
| 117 |
+
โ โ (Environment base) โ โ (Environment base) โ โ
|
| 118 |
+
โ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โ
|
| 119 |
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
### Core Components
|
| 123 |
+
|
| 124 |
+
#### 1. Web Interface
|
| 125 |
+
|
| 126 |
+
OpenEnv includes a built-in web interface for interactive environment exploration and debugging. The web interface provides:
|
| 127 |
+
|
| 128 |
+
- **Two-Pane Layout**: HumanAgent interaction on the left, state observation on the right
|
| 129 |
+
- **Real-time Updates**: WebSocket-based live updates without page refresh
|
| 130 |
+
- **Dynamic Forms**: Automatically generated action forms based on environment Action types
|
| 131 |
+
- **Action History**: Complete log of all actions taken and their results
|
| 132 |
+
|
| 133 |
+
The web interface is **conditionally enabled** based on environment variables:
|
| 134 |
+
|
| 135 |
+
- **Local Development**: Disabled by default for lightweight development
|
| 136 |
+
- **Manual Override**: Enable with `ENABLE_WEB_INTERFACE=true`
|
| 137 |
+
|
| 138 |
+
To use the web interface:
|
| 139 |
+
|
| 140 |
+
```python
|
| 141 |
+
from openenv.core.env_server import create_web_interface_app
|
| 142 |
+
from your_env.models import YourAction, YourObservation
|
| 143 |
+
from your_env.server.your_environment import YourEnvironment
|
| 144 |
+
|
| 145 |
+
env = YourEnvironment()
|
| 146 |
+
app = create_web_interface_app(env, YourAction, YourObservation)
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
When enabled, open `http://localhost:8000/web` in your browser to interact with the environment.
|
| 150 |
+
|
| 151 |
+
#### 2. Environment (Server-Side)
|
| 152 |
+
Base class for implementing environment logic:
|
| 153 |
+
- **`reset()`**: Initialize a new episode, returns initial `Observation`
|
| 154 |
+
- **`step(action)`**: Execute an `Action`, returns resulting `Observation`
|
| 155 |
+
- **`state()`**: Access episode metadata (`State` with episode_id, step_count, etc.)
|
| 156 |
+
|
| 157 |
+
#### 3. EnvClient (Client-Side)
|
| 158 |
+
Base class for environment communication:
|
| 159 |
+
- **Async by default**: Use `async with` and `await` for all operations
|
| 160 |
+
- **Sync wrapper**: Call `.sync()` to get a `SyncEnvClient` for synchronous usage
|
| 161 |
+
- Handles WebSocket connections to environment server
|
| 162 |
+
- Contains a utility to spin up a docker container locally for the corresponding environment
|
| 163 |
+
- Type-safe action/observation parsing
|
| 164 |
+
|
| 165 |
+
#### 4. Container Providers
|
| 166 |
+
Manage container deployment:
|
| 167 |
+
- `LocalDockerProvider`: Run containers on local Docker daemon
|
| 168 |
+
- `KubernetesProvider`: Deploy to K8s clusters (future)
|
| 169 |
+
|
| 170 |
+
#### 5. Models
|
| 171 |
+
Type-safe data structures:
|
| 172 |
+
- `Action`: Base class for environment actions
|
| 173 |
+
- `Observation`: Base class for environment observations
|
| 174 |
+
- `State`: Episode state tracking
|
| 175 |
+
- `StepResult`: Combines observation, reward, done flag
|
| 176 |
+
|
| 177 |
+
## Project Structure
|
| 178 |
+
|
| 179 |
+
### For Environment Creators
|
| 180 |
+
|
| 181 |
+
Use the CLI to quickly scaffold a new environment:
|
| 182 |
+
|
| 183 |
+
```bash
|
| 184 |
+
openenv init my_env
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
This creates the following structure:
|
| 188 |
+
|
| 189 |
+
```
|
| 190 |
+
my_env/
|
| 191 |
+
โโโ .dockerignore # Docker build exclusions
|
| 192 |
+
โโโ __init__.py # Export YourAction, YourObservation, YourEnv
|
| 193 |
+
โโโ models.py # Define Action, Observation, State dataclasses
|
| 194 |
+
โโโ client.py # Implement YourEnv(EnvClient)
|
| 195 |
+
โโโ README.md # Document your environment
|
| 196 |
+
โโโ openenv.yaml # Environment manifest
|
| 197 |
+
โโโ pyproject.toml # Dependencies and package configuration
|
| 198 |
+
โโโ outputs/ # Runtime outputs (logs, evals) - gitignored
|
| 199 |
+
โ โ๏ฟฝ๏ฟฝโ logs/
|
| 200 |
+
โ โโโ evals/
|
| 201 |
+
โโโ server/
|
| 202 |
+
โโโ your_environment.py # Implement YourEnvironment(Environment)
|
| 203 |
+
โโโ app.py # Create FastAPI app
|
| 204 |
+
โโโ requirements.txt # Dependencies for Docker (can be generated)
|
| 205 |
+
โโโ Dockerfile # Define container image
|
| 206 |
+
```
|
| 207 |
+
|
| 208 |
+
#### Dependency Management
|
| 209 |
+
|
| 210 |
+
OpenEnv uses `pyproject.toml` as the primary dependency specification:
|
| 211 |
+
|
| 212 |
+
- **Environment-level `pyproject.toml`**: Each environment defines its own dependencies
|
| 213 |
+
- **Root-level `pyproject.toml`**: Contains shared core dependencies (fastapi, pydantic, uvicorn)
|
| 214 |
+
- **Server `requirements.txt`**: Can be auto-generated from `pyproject.toml` for Docker builds
|
| 215 |
+
|
| 216 |
+
**Development Workflow:**
|
| 217 |
+
|
| 218 |
+
```bash
|
| 219 |
+
# Install environment in editable mode
|
| 220 |
+
cd my_env
|
| 221 |
+
pip install -e .
|
| 222 |
+
|
| 223 |
+
# Or using uv (faster)
|
| 224 |
+
uv pip install -e .
|
| 225 |
+
|
| 226 |
+
# Run server locally without Docker
|
| 227 |
+
uv run server --host 0.0.0.0 --port 8000
|
| 228 |
+
```
|
| 229 |
+
|
| 230 |
+
**Benefits:**
|
| 231 |
+
- โ
**Client-side extensions**: Modify client classes locally without repo changes
|
| 232 |
+
- โ
**Better dependency management**: Clear separation between environments
|
| 233 |
+
- โ
**Flexible workflows**: Use pip, uv, or Docker for different scenarios
|
| 234 |
+
- โ
**CI/CD ready**: Automated dependency generation and validation
|
| 235 |
+
|
| 236 |
+
See [`envs/README.md`](envs/README.md) for a complete guide on building environments.
|
| 237 |
+
|
| 238 |
+
### For Environment Users
|
| 239 |
+
|
| 240 |
+
To use an environment:
|
| 241 |
+
1. Install the client: `pip install git+https://huggingface.co/spaces/openenv/echo-env`
|
| 242 |
+
2. Import: `from echo_env import EchoAction, EchoEnv`
|
| 243 |
+
3. Use async (recommended) or sync API:
|
| 244 |
+
|
| 245 |
+
**Async (recommended):**
|
| 246 |
+
```python
|
| 247 |
+
async with EchoEnv(base_url="...") as client:
|
| 248 |
+
result = await client.reset()
|
| 249 |
+
result = await client.step(action)
|
| 250 |
+
```
|
| 251 |
+
|
| 252 |
+
**Sync (via `.sync()` wrapper):**
|
| 253 |
+
```python
|
| 254 |
+
with EchoEnv(base_url="...").sync() as client:
|
| 255 |
+
result = client.reset()
|
| 256 |
+
result = client.step(action)
|
| 257 |
+
```
|
| 258 |
+
|
| 259 |
+
See example scripts in `examples/` directory.
|
| 260 |
+
|
| 261 |
+
## CLI Commands
|
| 262 |
+
|
| 263 |
+
The OpenEnv CLI provides commands to manage environments:
|
| 264 |
+
|
| 265 |
+
- **`openenv init <env_name>`** - Initialize a new environment from template
|
| 266 |
+
- **`openenv push [--repo-id <repo>] [--private]`** - Deploy environment to Hugging Face Spaces
|
| 267 |
+
|
| 268 |
+
### Quick Start
|
| 269 |
+
|
| 270 |
+
```bash
|
| 271 |
+
# Create a new environment
|
| 272 |
+
openenv init my_game_env
|
| 273 |
+
|
| 274 |
+
# Deploy to Hugging Face (will prompt for login if needed)
|
| 275 |
+
cd my_game_env
|
| 276 |
+
openenv push
|
| 277 |
+
```
|
| 278 |
+
|
| 279 |
+
For detailed options: `openenv init --help` and `openenv push --help`.
|
| 280 |
+
|
| 281 |
+
## Design Principles
|
| 282 |
+
|
| 283 |
+
1. **Separation of Concerns**: Clear client-server boundaries
|
| 284 |
+
2. **Type Safety**: Strongly-typed actions, observations, and state
|
| 285 |
+
3. **Container Isolation**: Each environment runs in its own container
|
| 286 |
+
4. **Simple APIs**: Minimal, intuitive interfaces
|
| 287 |
+
|
| 288 |
+
## Development
|
| 289 |
+
|
| 290 |
+
### Installation
|
| 291 |
+
|
| 292 |
+
```bash
|
| 293 |
+
# Clone the repository
|
| 294 |
+
git clone https://github.com/meta-pytorch/OpenEnv.git
|
| 295 |
+
cd OpenEnv
|
| 296 |
+
|
| 297 |
+
# Install core package in editable mode
|
| 298 |
+
pip install -e .
|
| 299 |
+
# Or using uv (faster)
|
| 300 |
+
uv pip install -e .
|
| 301 |
+
```
|
| 302 |
+
|
| 303 |
+
### Running Tests
|
| 304 |
+
|
| 305 |
+
OpenEnv uses a modular dependency structure: the core package is minimal, and each environment has its own dependencies. This means some tests require environment-specific packages.
|
| 306 |
+
|
| 307 |
+
```bash
|
| 308 |
+
# Install pytest (required for running tests)
|
| 309 |
+
uv pip install pytest
|
| 310 |
+
|
| 311 |
+
# Run all tests (skips tests requiring uninstalled dependencies)
|
| 312 |
+
PYTHONPATH=src:envs uv run pytest tests/ -v --tb=short
|
| 313 |
+
|
| 314 |
+
# Run a specific test file
|
| 315 |
+
PYTHONPATH=src:envs uv run pytest tests/envs/test_echo_environment.py -v
|
| 316 |
+
```
|
| 317 |
+
|
| 318 |
+
**To run environment-specific tests**, install that environment's dependencies:
|
| 319 |
+
|
| 320 |
+
```bash
|
| 321 |
+
# Example: Install coding_env with dev dependencies (includes smolagents + pytest)
|
| 322 |
+
uv pip install -e "envs/coding_env[dev]"
|
| 323 |
+
|
| 324 |
+
# Then run coding_env tests
|
| 325 |
+
PYTHONPATH=src:envs uv run pytest tests/envs/test_python_codeact_rewards.py -v
|
| 326 |
+
```
|
| 327 |
+
|
| 328 |
+
Tests will be automatically skipped if their required dependencies aren't installed.
|
| 329 |
+
|
| 330 |
+
## Requirements
|
| 331 |
+
|
| 332 |
+
- Python 3.10+
|
| 333 |
+
- Docker Desktop or Docker Engine
|
| 334 |
+
- FastAPI >= 0.104.0
|
| 335 |
+
- Uvicorn >= 0.24.0
|
| 336 |
+
- Requests >= 2.25.0
|
| 337 |
+
- Environment-specific dependencies (e.g., smolagents for coding_env)
|
| 338 |
+
|
| 339 |
+
## Supported RL Tools
|
| 340 |
+
The goal of this project is to support a broad set of open and closed tools to help standardize the agentic RL community. If you have a project that supports OpenEnv environments, please put up a PR to add your tool name along with a link to your documentation.
|
| 341 |
+
|
| 342 |
+
### torchforge
|
| 343 |
+
See GRPO BlackJack training example: [`examples/grpo_blackjack/`](examples/grpo_blackjack/)
|
| 344 |
+
|
| 345 |
+
### TRL
|
| 346 |
+
See the [TRL example](https://huggingface.co/docs/trl/openenv) on how to integrate OpenEnv environments with GRPO training.
|
| 347 |
+
|
| 348 |
+
### Unsloth
|
| 349 |
+
See the 2048 game example based on gpt-oss: [Colab notebook](https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/OpenEnv_gpt_oss_(20B)_Reinforcement_Learning_2048_Game.ipynb)
|
| 350 |
+
|
| 351 |
+
### SkyRL
|
| 352 |
+
See the [SkyRL example](https://skyrl.readthedocs.io/en/latest/examples/openenv.html) on how to train on OpenEnv environments with SkyRL.
|
| 353 |
+
|
| 354 |
+
### ART
|
| 355 |
+
See the [ART example](https://art.openpipe.ai/integrations/openenv-integration) on how OpenEnv environments can be used to train models with ART.
|
| 356 |
+
|
| 357 |
+
### Oumi
|
| 358 |
+
See the [Oumi example](https://github.com/oumi-ai/oumi/blob/main/notebooks/Oumi%20-%20OpenEnv%20GRPO%20with%20trl.ipynb) on how OpenEnv environments can be used to train models with Oumi.
|
| 359 |
+
|
| 360 |
+
## Example Environments
|
| 361 |
+
|
| 362 |
+
| Environment | Description |
|
| 363 |
+
|---|---|
|
| 364 |
+
| [Echo Environment](envs/echo_env/README.md) | Echoes back messages with metadata. Ideal for testing HTTP server infrastructure, learning framework basics, and verifying container deployment. |
|
| 365 |
+
| [Coding Environment](envs/coding_env/README.md) | Sandboxed Python code execution via smolagents. Captures stdout/stderr/exit codes, supports persistent episode context, and provides detailed error handling. |
|
| 366 |
+
| [Chess Environment](envs/chess_env/README.md) | Chess RL environment with configurable opponents and full rules support. |
|
| 367 |
+
| [Atari Environment](envs/atari_env/README.md) | Classic Arcade Learning Environment tasks for RL benchmarking. |
|
| 368 |
+
| [FinRL Environment](envs/finrl_env/README.md) | Financial market simulations for algorithmic trading experiments. |
|
| 369 |
+
|
| 370 |
+
> Browse the full catalog of community environments at [meta-pytorch.org/OpenEnv/environments](https://meta-pytorch.org/OpenEnv/environments/).
|
| 371 |
+
|
| 372 |
+
## Community Support & Acknowledgments
|
| 373 |
+
This is an open and community-centric project. If you would like to add your name here, please put up a pull request and tag @jspisak for review. Ty!!
|
| 374 |
+
|
| 375 |
+
Supporters include: Meta-PyTorch, Hugging Face, [Scaler AI Labs](https://scalerailabs.com), [Patronus AI](https://patronus.ai), [Surge AI](https://surgehq.ai), [LastMile AI](https://www.lastmileai.dev), Unsloth AI, Reflection AI, vLLM, SkyRL (UC-Berkeley), LightningAI, Axolotl AI, Stanford Scaling Intelligence Lab, Mithril, [OpenMined](https://openmined.org/), [Fleet AI](https://fleetai.com), [Halluminate](https://halluminate.ai/), [Turing](https://www.turing.com/), [Scale AI](https://scale.com/) ..
|
| 376 |
+
|
| 377 |
+
And we'd also like to acknowledge the team at Farama Foundation as the OpenEnv API was heavily inspired by the work you all have done on Gymnasium. Cheers!
|
| 378 |
+
|
| 379 |
+
## License
|
| 380 |
+
|
| 381 |
+
BSD 3-Clause License (see [LICENSE](./LICENSE) file)
|
OpenEnv/docs/Makefile
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Minimal makefile for Sphinx documentation
|
| 2 |
+
#
|
| 3 |
+
|
| 4 |
+
# You can set these variables from the command line.
|
| 5 |
+
SPHINXOPTS =
|
| 6 |
+
SPHINXBUILD = sphinx-build
|
| 7 |
+
SPHINXPROJ = OpenEnv
|
| 8 |
+
SOURCEDIR = source
|
| 9 |
+
BUILDDIR = _build
|
| 10 |
+
|
| 11 |
+
# Put it first so that "make" without argument is like "make help".
|
| 12 |
+
|
| 13 |
+
html-noplot:
|
| 14 |
+
$(SPHINXBUILD) -D plot_gallery=0 -b html $(SPHINXOPTS) "$(SOURCEDIR)" "$(BUILDDIR)/html"
|
| 15 |
+
|
| 16 |
+
html-stable:
|
| 17 |
+
RELEASE=true $(MAKE) html
|
| 18 |
+
|
| 19 |
+
help:
|
| 20 |
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
| 21 |
+
|
| 22 |
+
.PHONY: help Makefile
|
| 23 |
+
|
| 24 |
+
%: Makefile
|
| 25 |
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
OpenEnv/docs/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Building the Docs Locally
|
| 2 |
+
|
| 3 |
+
## Prerequisites
|
| 4 |
+
|
| 5 |
+
- Python 3.11+
|
| 6 |
+
|
| 7 |
+
## Setup
|
| 8 |
+
|
| 9 |
+
Install OpenEnv with the docs dependencies:
|
| 10 |
+
|
| 11 |
+
```bash
|
| 12 |
+
pip install -e ".[docs]"
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
## Build
|
| 16 |
+
|
| 17 |
+
From the `docs/` directory:
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
cd docs
|
| 21 |
+
make html
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
The output will be in `docs/_build/html/`.
|
| 25 |
+
|
| 26 |
+
## Preview
|
| 27 |
+
|
| 28 |
+
From the repo root, start a local server:
|
| 29 |
+
|
| 30 |
+
```bash
|
| 31 |
+
cd docs/_build/html
|
| 32 |
+
python -m http.server 8000
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
Then open [http://localhost:8000](http://localhost:8000) in your browser.
|
| 36 |
+
|
| 37 |
+
### Build Variants
|
| 38 |
+
|
| 39 |
+
| Command | Description |
|
| 40 |
+
|---------|-------------|
|
| 41 |
+
| `make html` | Full build with Sphinx Gallery execution |
|
| 42 |
+
| `make html-noplot` | Skip gallery execution (faster) |
|
| 43 |
+
| `make html-stable` | Build as a versioned release |
|
| 44 |
+
| `make clean html` | Clean rebuild from scratch |
|
| 45 |
+
|
| 46 |
+
## How the Getting Started Section Builds
|
| 47 |
+
|
| 48 |
+
The Getting Started section is powered by [Sphinx Gallery](https://sphinx-gallery.github.io/). Source files live in `docs/source/getting_started/`:
|
| 49 |
+
|
| 50 |
+
- **`plot_*.py`** โ executable Python scripts that Sphinx Gallery converts into rendered notebook pages
|
| 51 |
+
- **`*.md`** โ static Markdown pages (environment-builder, contributing-envs)
|
| 52 |
+
- **`README.rst`** โ gallery index template
|
| 53 |
+
|
| 54 |
+
During the build, Sphinx Gallery processes these sources and writes the output into a generated `auto_getting_started/` directory. A custom `copy_md_pages_to_gallery` hook in `conf.py` copies the static `.md` pages into that same output directory so they appear alongside the gallery notebooks in the left nav.
|
| 55 |
+
|
| 56 |
+
Because `getting_started/*.md` is in `exclude_patterns` in `conf.py`, Sphinx only generates HTML from the `auto_getting_started/` output โ not from the source directory directly. This means all internal links to Getting Started pages must use the `auto_getting_started/` path (e.g. `auto_getting_started/environment-builder.md`). Linking to `getting_started/` will 404.
|
| 57 |
+
|
| 58 |
+
The copy hook runs on the `builder-inited` event, so static pages are available in every build variant including `make html-noplot`. That flag only skips executing the `plot_*.py` gallery scripts; it does not skip the page copy.
|
| 59 |
+
|
| 60 |
+
## Adding an Environment to the Docs
|
| 61 |
+
|
| 62 |
+
Every environment page is generated from the environment's own `README.md` using a Sphinx `{include}` directive. There are three steps:
|
| 63 |
+
|
| 64 |
+
### 1. Write the environment README
|
| 65 |
+
|
| 66 |
+
Your environment must have a `README.md` at `envs/<name>/README.md`. This file is the single source of truth โ it renders on GitHub and is pulled into the docs site at build time.
|
| 67 |
+
|
| 68 |
+
Include HuggingFace frontmatter at the top, followed by a `# Title` heading (this becomes the page title and left nav label):
|
| 69 |
+
|
| 70 |
+
```markdown
|
| 71 |
+
---
|
| 72 |
+
title: My Environment
|
| 73 |
+
emoji: ๐ฎ
|
| 74 |
+
colorFrom: blue
|
| 75 |
+
colorTo: green
|
| 76 |
+
sdk: docker
|
| 77 |
+
pinned: false
|
| 78 |
+
app_port: 8000
|
| 79 |
+
base_path: /web
|
| 80 |
+
tags:
|
| 81 |
+
- openenv
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
# My Environment
|
| 85 |
+
|
| 86 |
+
Description, quick start, action/observation docs, etc.
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
### 2. Create the doc page
|
| 90 |
+
|
| 91 |
+
Create `docs/source/environments/<name>.md` with exactly this content:
|
| 92 |
+
|
| 93 |
+
````markdown
|
| 94 |
+
```{include} ../../../envs/<name>/README.md
|
| 95 |
+
```
|
| 96 |
+
````
|
| 97 |
+
|
| 98 |
+
This is the only pattern used โ all 29 environment doc pages follow it. Do not add local headings or other content.
|
| 99 |
+
|
| 100 |
+
### 3. Add a card and toctree entry
|
| 101 |
+
|
| 102 |
+
Edit `docs/source/environments.md` to add two things:
|
| 103 |
+
|
| 104 |
+
* **A card** inside the existing `{grid}` block (place alphabetically):
|
| 105 |
+
|
| 106 |
+
````markdown
|
| 107 |
+
````{grid-item-card} My Environment
|
| 108 |
+
:class-card: sd-border-1
|
| 109 |
+
|
| 110 |
+
Short one-line description of the environment.
|
| 111 |
+
|
| 112 |
+
+++
|
| 113 |
+
```{button-link} environments/<name>.html
|
| 114 |
+
:color: primary
|
| 115 |
+
:outline:
|
| 116 |
+
|
| 117 |
+
{octicon}`file;1em` Docs
|
| 118 |
+
```
|
| 119 |
+
```{button-link} https://huggingface.co/spaces/<org>/<name>
|
| 120 |
+
:color: warning
|
| 121 |
+
:outline:
|
| 122 |
+
|
| 123 |
+
๐ค Hugging Face
|
| 124 |
+
```
|
| 125 |
+
````
|
| 126 |
+
|
| 127 |
+
The Hugging Face button is optional โ omit it if the environment isn't deployed to a Space.
|
| 128 |
+
|
| 129 |
+
* **A toctree entry** in the `{toctree}` block at the bottom of the file (place alphabetically):
|
| 130 |
+
|
| 131 |
+
```
|
| 132 |
+
environments/<name>
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
### Verify
|
| 136 |
+
|
| 137 |
+
Rebuild and check that the environment appears in the left nav and the catalog grid:
|
| 138 |
+
|
| 139 |
+
```bash
|
| 140 |
+
cd docs && make clean html
|
| 141 |
+
cd _build/html && python -m http.server 8000
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
## Version Switcher
|
| 145 |
+
|
| 146 |
+
The docs site includes a version dropdown in the top-left navbar (next to the logo) powered by the `pytorch-sphinx-theme2` theme. It is configured by three pieces:
|
| 147 |
+
|
| 148 |
+
### `versions.json`
|
| 149 |
+
|
| 150 |
+
The file `docs/source/_static/versions.json` lists all published versions. Each entry has a `name` (display label), `version` (matching key), `url` (root URL for that version), and optionally `preferred: true` for the default. Currently it contains only the `main` development build:
|
| 151 |
+
|
| 152 |
+
```json
|
| 153 |
+
[
|
| 154 |
+
{
|
| 155 |
+
"name": "main",
|
| 156 |
+
"version": "main",
|
| 157 |
+
"url": "https://meta-pytorch.org/OpenEnv/",
|
| 158 |
+
"preferred": true
|
| 159 |
+
}
|
| 160 |
+
]
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
### Build-time version detection
|
| 164 |
+
|
| 165 |
+
`conf.py` reads the version from `pyproject.toml` and uses the `RELEASE` environment variable to decide which mode to build in:
|
| 166 |
+
|
| 167 |
+
- **`make html`** (default) โ builds as `main`. The switcher highlights the "main" entry.
|
| 168 |
+
- **`make html-stable`** โ sets `RELEASE=true`. The version is extracted from `pyproject.toml` (e.g. `0.2` from `0.2.2.dev0`) and the switcher highlights that version's entry.
|
| 169 |
+
|
| 170 |
+
### Publishing a new version
|
| 171 |
+
|
| 172 |
+
When cutting a release:
|
| 173 |
+
|
| 174 |
+
1. Ensure `pyproject.toml` has the release version (e.g. `0.2.0`)
|
| 175 |
+
2. Build with `make html-stable` โ this produces docs tagged as version `0.2`
|
| 176 |
+
3. Deploy the output to a versioned path (e.g. `https://meta-pytorch.org/OpenEnv/0.2/`)
|
| 177 |
+
4. Add an entry to `docs/source/_static/versions.json`:
|
| 178 |
+
|
| 179 |
+
```json
|
| 180 |
+
{
|
| 181 |
+
"name": "0.2",
|
| 182 |
+
"version": "0.2",
|
| 183 |
+
"url": "https://meta-pytorch.org/OpenEnv/0.2/"
|
| 184 |
+
}
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
5. Rebuild and redeploy `main` so its copy of `versions.json` includes the new entry
|
| 188 |
+
|
| 189 |
+
The switcher on every version of the site fetches `versions.json` at page load, so all versions see the full list once the file is updated.
|
OpenEnv/docs/source/_static/versions.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"name": "main ",
|
| 4 |
+
"version": "main",
|
| 5 |
+
"url": "https://meta-pytorch.org/OpenEnv/",
|
| 6 |
+
"preferred": true
|
| 7 |
+
}
|
| 8 |
+
]
|
OpenEnv/docs/source/auto_discovery.md
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Auto-Discovery
|
| 2 |
+
|
| 3 |
+
OpenEnv provides a HuggingFace-style auto-discovery API that makes it easy to work with environments without manual imports.
|
| 4 |
+
|
| 5 |
+
## Overview
|
| 6 |
+
|
| 7 |
+
The auto-discovery system provides two main classes:
|
| 8 |
+
|
| 9 |
+
- **`AutoEnv`**: Automatically loads and instantiates environment clients
|
| 10 |
+
- **`AutoAction`**: Automatically loads action classes for environments
|
| 11 |
+
|
| 12 |
+
Both classes work with:
|
| 13 |
+
- **Local packages**: Installed via `pip install openenv-<env-name>`
|
| 14 |
+
- **HuggingFace Hub**: Environments hosted on HuggingFace Spaces
|
| 15 |
+
|
| 16 |
+
## Quick Start
|
| 17 |
+
|
| 18 |
+
### Basic Usage
|
| 19 |
+
|
| 20 |
+
Instead of manually importing specific environment classes:
|
| 21 |
+
|
| 22 |
+
```python
|
| 23 |
+
# Old way - requires knowing the module path
|
| 24 |
+
from coding_env import CodingEnv, CodeAction
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
You can now use the auto-discovery API:
|
| 28 |
+
|
| 29 |
+
```python
|
| 30 |
+
from openenv import AutoEnv, AutoAction
|
| 31 |
+
|
| 32 |
+
# Create environment (returns async client)
|
| 33 |
+
env = AutoEnv.from_env("coding-env")
|
| 34 |
+
|
| 35 |
+
# Get action class
|
| 36 |
+
CodeAction = AutoAction.from_env("coding-env")
|
| 37 |
+
|
| 38 |
+
# Use with sync wrapper for simple scripts
|
| 39 |
+
with env.sync() as client:
|
| 40 |
+
result = client.reset()
|
| 41 |
+
action = CodeAction(code="print('Hello, OpenEnv!')")
|
| 42 |
+
step_result = client.step(action)
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
## AutoEnv API
|
| 46 |
+
|
| 47 |
+
### `AutoEnv.from_env(name, **kwargs)`
|
| 48 |
+
|
| 49 |
+
Create an environment client from a name or HuggingFace Hub repository.
|
| 50 |
+
|
| 51 |
+
**Parameters:**
|
| 52 |
+
- `name`: Environment name or Hub repo ID
|
| 53 |
+
- Local: `"coding"`, `"coding-env"`, `"coding_env"`
|
| 54 |
+
- Hub: `"meta-pytorch/coding-env"`, `"username/env-name"`
|
| 55 |
+
- `base_url`: Optional base URL for HTTP connection
|
| 56 |
+
- `docker_image`: Optional Docker image name (overrides default)
|
| 57 |
+
- `container_provider`: Optional container provider
|
| 58 |
+
- `wait_timeout`: Timeout for container startup (default: 30s)
|
| 59 |
+
- `env_vars`: Optional environment variables for the container
|
| 60 |
+
- `**kwargs`: Additional arguments passed to the client class
|
| 61 |
+
|
| 62 |
+
**Returns:** Instance of the environment client class
|
| 63 |
+
|
| 64 |
+
**Examples:**
|
| 65 |
+
|
| 66 |
+
```python
|
| 67 |
+
from openenv import AutoEnv
|
| 68 |
+
|
| 69 |
+
# From installed package
|
| 70 |
+
env = AutoEnv.from_env("coding-env")
|
| 71 |
+
|
| 72 |
+
# From HuggingFace Hub
|
| 73 |
+
env = AutoEnv.from_env("meta-pytorch/coding-env")
|
| 74 |
+
|
| 75 |
+
# With custom configuration
|
| 76 |
+
env = AutoEnv.from_env(
|
| 77 |
+
"coding",
|
| 78 |
+
docker_image="my-coding-env:v2",
|
| 79 |
+
wait_timeout=60.0,
|
| 80 |
+
env_vars={"DEBUG": "1"}
|
| 81 |
+
)
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
### `AutoEnv.list_environments()`
|
| 85 |
+
|
| 86 |
+
List all available environments.
|
| 87 |
+
|
| 88 |
+
```python
|
| 89 |
+
from openenv import AutoEnv
|
| 90 |
+
|
| 91 |
+
AutoEnv.list_environments()
|
| 92 |
+
# Output:
|
| 93 |
+
# Available Environments:
|
| 94 |
+
# ----------------------------------------------------------------------
|
| 95 |
+
# coding : Coding environment for OpenEnv (v0.1.0)
|
| 96 |
+
# echo : echo_env environment (v0.1.0)
|
| 97 |
+
# browsergym : BrowserGym environment (v0.1.0)
|
| 98 |
+
# ...
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
### `AutoEnv.get_env_info(name)`
|
| 102 |
+
|
| 103 |
+
Get detailed information about an environment.
|
| 104 |
+
|
| 105 |
+
```python
|
| 106 |
+
from openenv import AutoEnv
|
| 107 |
+
|
| 108 |
+
info = AutoEnv.get_env_info("coding")
|
| 109 |
+
print(f"Description: {info['description']}")
|
| 110 |
+
print(f"Version: {info['version']}")
|
| 111 |
+
print(f"Docker Image: {info['default_image']}")
|
| 112 |
+
print(f"Client Class: {info['env_class']}")
|
| 113 |
+
print(f"Action Class: {info['action_class']}")
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
+
### `AutoEnv.get_env_class(name)`
|
| 117 |
+
|
| 118 |
+
Get the environment class (not an instance).
|
| 119 |
+
|
| 120 |
+
```python
|
| 121 |
+
from openenv import AutoEnv
|
| 122 |
+
|
| 123 |
+
CodingEnv = AutoEnv.get_env_class("coding")
|
| 124 |
+
# Now you can instantiate it yourself with custom parameters
|
| 125 |
+
env = CodingEnv.from_docker_image("coding-env:latest", wait_timeout=60.0)
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
## AutoAction API
|
| 129 |
+
|
| 130 |
+
### `AutoAction.from_env(name)`
|
| 131 |
+
|
| 132 |
+
Get the Action class from an environment name or HuggingFace Hub repository.
|
| 133 |
+
|
| 134 |
+
**Parameters:**
|
| 135 |
+
- `name`: Environment name or Hub repo ID
|
| 136 |
+
|
| 137 |
+
**Returns:** Action class (not an instance!)
|
| 138 |
+
|
| 139 |
+
**Examples:**
|
| 140 |
+
|
| 141 |
+
```python
|
| 142 |
+
from openenv import AutoAction
|
| 143 |
+
|
| 144 |
+
# From installed package
|
| 145 |
+
CodeAction = AutoAction.from_env("coding-env")
|
| 146 |
+
action = CodeAction(code="print('Hello!')")
|
| 147 |
+
|
| 148 |
+
# From HuggingFace Hub
|
| 149 |
+
CodeAction = AutoAction.from_env("meta-pytorch/coding-env")
|
| 150 |
+
|
| 151 |
+
# Different name formats work
|
| 152 |
+
EchoAction = AutoAction.from_env("echo")
|
| 153 |
+
EchoAction = AutoAction.from_env("echo-env")
|
| 154 |
+
EchoAction = AutoAction.from_env("echo_env")
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
### `AutoAction.from_hub(env_name)`
|
| 158 |
+
|
| 159 |
+
Alias for `from_env()` for backward compatibility.
|
| 160 |
+
|
| 161 |
+
```python
|
| 162 |
+
from openenv import AutoAction
|
| 163 |
+
|
| 164 |
+
CodeAction = AutoAction.from_env("coding")
|
| 165 |
+
action = CodeAction(code="x = 5 + 3")
|
| 166 |
+
```
|
| 167 |
+
|
| 168 |
+
### `AutoAction.list_actions()`
|
| 169 |
+
|
| 170 |
+
List all available action classes.
|
| 171 |
+
|
| 172 |
+
```python
|
| 173 |
+
from openenv import AutoAction
|
| 174 |
+
|
| 175 |
+
AutoAction.list_actions()
|
| 176 |
+
# Output:
|
| 177 |
+
# Available Action Classes:
|
| 178 |
+
# ----------------------------------------------------------------------
|
| 179 |
+
# coding : CodeAction
|
| 180 |
+
# echo : EchoAction
|
| 181 |
+
# browsergym : BrowsergymAction
|
| 182 |
+
# ...
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
### `AutoAction.get_action_info(name)`
|
| 186 |
+
|
| 187 |
+
Get detailed information about an action class.
|
| 188 |
+
|
| 189 |
+
```python
|
| 190 |
+
from openenv import AutoAction
|
| 191 |
+
|
| 192 |
+
info = AutoAction.get_action_info("coding")
|
| 193 |
+
print(f"Action Class: {info['action_class']}")
|
| 194 |
+
print(f"Module: {info['module']}")
|
| 195 |
+
```
|
| 196 |
+
|
| 197 |
+
## HuggingFace Hub Integration
|
| 198 |
+
|
| 199 |
+
### Loading from HuggingFace Spaces
|
| 200 |
+
|
| 201 |
+
AutoEnv can automatically connect to environments running on HuggingFace Spaces:
|
| 202 |
+
|
| 203 |
+
```python
|
| 204 |
+
from openenv import AutoEnv, AutoAction
|
| 205 |
+
|
| 206 |
+
# Load from HuggingFace Space
|
| 207 |
+
env = AutoEnv.from_env("username/coding-env-test")
|
| 208 |
+
|
| 209 |
+
# Get action class
|
| 210 |
+
CodeAction = AutoAction.from_env("username/coding-env-test")
|
| 211 |
+
|
| 212 |
+
# Use with sync wrapper
|
| 213 |
+
with env.sync() as client:
|
| 214 |
+
result = client.reset()
|
| 215 |
+
action = CodeAction(code="print('Hello from HF Space!')")
|
| 216 |
+
step_result = client.step(action)
|
| 217 |
+
print(f"Output: {step_result.observation.stdout}")
|
| 218 |
+
```
|
| 219 |
+
|
| 220 |
+
The system automatically:
|
| 221 |
+
1. Detects HuggingFace repo IDs (format: `username/repo-name`)
|
| 222 |
+
2. Resolves the Space URL (e.g., `https://username-repo-name.hf.space`)
|
| 223 |
+
3. Checks if the Space is running and accessible
|
| 224 |
+
4. Installs the environment package using `git+` URL (prompts for confirmation)
|
| 225 |
+
5. Connects to the running Space
|
| 226 |
+
|
| 227 |
+
### Security: Remote Code Installation
|
| 228 |
+
|
| 229 |
+
When loading environments from HuggingFace Hub, AutoEnv needs to install Python code from the remote repository. Since this executes code from the internet, AutoEnv will prompt for confirmation before installing:
|
| 230 |
+
|
| 231 |
+
```
|
| 232 |
+
============================================================
|
| 233 |
+
SECURITY WARNING: Remote Code Installation
|
| 234 |
+
============================================================
|
| 235 |
+
You are about to install code from a remote repository:
|
| 236 |
+
Repository: username/coding-env-test
|
| 237 |
+
Source: https://huggingface.co/spaces/username/coding-env-test
|
| 238 |
+
|
| 239 |
+
This will execute code from the internet on your machine.
|
| 240 |
+
Only proceed if you trust the source.
|
| 241 |
+
============================================================
|
| 242 |
+
|
| 243 |
+
Do you want to proceed? [y/N]:
|
| 244 |
+
```
|
| 245 |
+
|
| 246 |
+
To skip the confirmation prompt, you can either:
|
| 247 |
+
|
| 248 |
+
1. **Use the `trust_remote_code` parameter:**
|
| 249 |
+
```python
|
| 250 |
+
env = AutoEnv.from_env("username/coding-env", trust_remote_code=True)
|
| 251 |
+
```
|
| 252 |
+
|
| 253 |
+
2. **Set the environment variable:**
|
| 254 |
+
```bash
|
| 255 |
+
export OPENENV_TRUST_REMOTE_CODE=1
|
| 256 |
+
python your_script.py
|
| 257 |
+
```
|
| 258 |
+
|
| 259 |
+
### Package Installation
|
| 260 |
+
|
| 261 |
+
AutoEnv uses `uv pip` if available, otherwise falls back to standard `pip`. This ensures compatibility with different Python environments:
|
| 262 |
+
|
| 263 |
+
```bash
|
| 264 |
+
# If uv is installed, AutoEnv uses:
|
| 265 |
+
uv pip install git+https://huggingface.co/spaces/username/coding-env
|
| 266 |
+
|
| 267 |
+
# Otherwise, it uses:
|
| 268 |
+
pip install git+https://huggingface.co/spaces/username/coding-env
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
## Complete Workflow Example
|
| 272 |
+
|
| 273 |
+
Here's a complete example showing the auto-discovery workflow:
|
| 274 |
+
|
| 275 |
+
```python
|
| 276 |
+
from openenv import AutoEnv, AutoAction
|
| 277 |
+
|
| 278 |
+
# 1. List available environments
|
| 279 |
+
print("Available environments:")
|
| 280 |
+
AutoEnv.list_environments()
|
| 281 |
+
|
| 282 |
+
# 2. Create environment and get action class
|
| 283 |
+
env = AutoEnv.from_env("coding-env")
|
| 284 |
+
CodeAction = AutoAction.from_env("coding-env")
|
| 285 |
+
|
| 286 |
+
# 3. Use with sync wrapper for simple scripts
|
| 287 |
+
with env.sync() as client:
|
| 288 |
+
# Reset environment
|
| 289 |
+
result = client.reset()
|
| 290 |
+
print(f"Environment ready: {result.observation}")
|
| 291 |
+
|
| 292 |
+
# Execute actions
|
| 293 |
+
action = CodeAction(code="""
|
| 294 |
+
def fibonacci(n):
|
| 295 |
+
if n <= 1:
|
| 296 |
+
return n
|
| 297 |
+
return fibonacci(n-1) + fibonacci(n-2)
|
| 298 |
+
|
| 299 |
+
print(f"Fibonacci(10) = {fibonacci(10)}")
|
| 300 |
+
""")
|
| 301 |
+
|
| 302 |
+
step_result = client.step(action)
|
| 303 |
+
print(f"Output:\n{step_result.observation.stdout}")
|
| 304 |
+
```
|
| 305 |
+
|
| 306 |
+
For async usage (recommended for production):
|
| 307 |
+
|
| 308 |
+
```python
|
| 309 |
+
import asyncio
|
| 310 |
+
from coding_env import CodingEnv, CodeAction
|
| 311 |
+
|
| 312 |
+
async def main():
|
| 313 |
+
async with CodingEnv(base_url="http://localhost:8000") as client:
|
| 314 |
+
result = await client.reset()
|
| 315 |
+
result = await client.step(CodeAction(code="print('async!')"))
|
| 316 |
+
print(result.observation.stdout)
|
| 317 |
+
|
| 318 |
+
asyncio.run(main())
|
| 319 |
+
```
|
| 320 |
+
|
| 321 |
+
## Error Handling
|
| 322 |
+
|
| 323 |
+
The auto-discovery API provides helpful error messages:
|
| 324 |
+
|
| 325 |
+
```python
|
| 326 |
+
from openenv import AutoEnv
|
| 327 |
+
|
| 328 |
+
try:
|
| 329 |
+
env = AutoEnv.from_env("nonexistent-env")
|
| 330 |
+
except ValueError as e:
|
| 331 |
+
print(e)
|
| 332 |
+
# Output:
|
| 333 |
+
# Unknown environment 'nonexistent'.
|
| 334 |
+
# Did you mean: coding?
|
| 335 |
+
# Available environments: atari, browsergym, chat, coding, ...
|
| 336 |
+
```
|
| 337 |
+
|
| 338 |
+
For typos, it suggests similar environment names:
|
| 339 |
+
|
| 340 |
+
```python
|
| 341 |
+
try:
|
| 342 |
+
env = AutoEnv.from_env("cooding-env") # Typo
|
| 343 |
+
except ValueError as e:
|
| 344 |
+
print(e)
|
| 345 |
+
# Output:
|
| 346 |
+
# Unknown environment 'cooding'.
|
| 347 |
+
# Did you mean: coding?
|
| 348 |
+
# Available environments: ...
|
| 349 |
+
```
|
| 350 |
+
|
| 351 |
+
## Flexible Name Formats
|
| 352 |
+
|
| 353 |
+
AutoEnv accepts multiple name formats:
|
| 354 |
+
|
| 355 |
+
```python
|
| 356 |
+
from openenv import AutoEnv
|
| 357 |
+
|
| 358 |
+
# All of these work and refer to the same environment:
|
| 359 |
+
env = AutoEnv.from_env("coding") # Simple name
|
| 360 |
+
env = AutoEnv.from_env("coding-env") # With suffix
|
| 361 |
+
env = AutoEnv.from_env("coding_env") # With underscore
|
| 362 |
+
env = AutoEnv.from_env("coding-env:latest") # With tag (ignored)
|
| 363 |
+
```
|
| 364 |
+
|
| 365 |
+
## How It Works
|
| 366 |
+
|
| 367 |
+
The auto-discovery system works by:
|
| 368 |
+
|
| 369 |
+
1. **Package Discovery**: Uses `importlib.metadata` to find installed `openenv-*` packages
|
| 370 |
+
2. **Manifest Loading**: Reads `openenv.yaml` files from package resources
|
| 371 |
+
3. **Caching**: Caches discovery results for performance
|
| 372 |
+
4. **Lazy Loading**: Only imports classes when actually needed
|
| 373 |
+
5. **Hub Support**: Downloads and installs packages from HuggingFace Hub on-demand
|
| 374 |
+
|
| 375 |
+
### Environment Packages
|
| 376 |
+
|
| 377 |
+
Environments are distributed as installable Python packages:
|
| 378 |
+
|
| 379 |
+
```bash
|
| 380 |
+
# Install an environment
|
| 381 |
+
pip install openenv-coding-env
|
| 382 |
+
|
| 383 |
+
# Now it's automatically discoverable
|
| 384 |
+
python -c "from openenv import AutoEnv; AutoEnv.list_environments()"
|
| 385 |
+
```
|
| 386 |
+
|
| 387 |
+
Each environment package includes:
|
| 388 |
+
- Client classes (e.g., `CodingEnv`)
|
| 389 |
+
- Action/Observation models (e.g., `CodeAction`, `CodeObservation`)
|
| 390 |
+
- Server Docker image
|
| 391 |
+
- `openenv.yaml` manifest describing the environment
|
| 392 |
+
|
| 393 |
+
### Manifest Format
|
| 394 |
+
|
| 395 |
+
Each environment includes an `openenv.yaml` file:
|
| 396 |
+
|
| 397 |
+
```yaml
|
| 398 |
+
name: coding_env
|
| 399 |
+
version: 0.1.0
|
| 400 |
+
description: Coding environment for OpenEnv
|
| 401 |
+
|
| 402 |
+
client:
|
| 403 |
+
class_name: CodingEnv
|
| 404 |
+
module: coding_env.client
|
| 405 |
+
|
| 406 |
+
action:
|
| 407 |
+
class_name: CodeAction
|
| 408 |
+
module: coding_env.client
|
| 409 |
+
|
| 410 |
+
observation:
|
| 411 |
+
class_name: CodeObservation
|
| 412 |
+
module: coding_env.client
|
| 413 |
+
|
| 414 |
+
default_image: coding-env:latest
|
| 415 |
+
spec_version: 1
|
| 416 |
+
```
|
| 417 |
+
|
| 418 |
+
## Benefits
|
| 419 |
+
|
| 420 |
+
โ
**Simple**: No need to know which module to import from
|
| 421 |
+
โ
**Flexible**: Works with local packages and HuggingFace Hub
|
| 422 |
+
โ
**Discoverable**: List and explore available environments
|
| 423 |
+
โ
**Type-Safe**: Returns properly typed environment classes
|
| 424 |
+
โ
**HuggingFace-style**: Familiar API for ML practitioners
|
| 425 |
+
โ
**Performant**: Caching and lazy loading for efficiency
|
| 426 |
+
|
| 427 |
+
## See Also
|
| 428 |
+
|
| 429 |
+
- [Environment Builder Guide](auto_getting_started/environment-builder.md) - How to create your own environments
|
| 430 |
+
- [Core API Documentation](core.md) - Low-level API details
|
| 431 |
+
- [HuggingFace Hub](https://huggingface.co/meta-pytorch) - Pre-built environments
|
OpenEnv/docs/source/cli.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLI
|
| 2 |
+
|
| 3 |
+
The `openenv` CLI provides a set of commands for building, validating, and pushing environments to Hugging Face Spaces or a custom Docker registry. For an end-to-end tutorial on building environments with OpenEnv, see the [building an environment](auto_getting_started/environment-builder.md) guide.
|
| 4 |
+
|
| 5 |
+
## `openenv init`
|
| 6 |
+
|
| 7 |
+
```{eval-rst}
|
| 8 |
+
.. automodule:: openenv.cli.commands.init
|
| 9 |
+
:members:
|
| 10 |
+
:undoc-members:
|
| 11 |
+
:show-inheritance:
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
## `openenv build`
|
| 15 |
+
|
| 16 |
+
```{eval-rst}
|
| 17 |
+
.. automodule:: openenv.cli.commands.build
|
| 18 |
+
:members:
|
| 19 |
+
:undoc-members:
|
| 20 |
+
:show-inheritance:
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## `openenv validate`
|
| 24 |
+
|
| 25 |
+
```{eval-rst}
|
| 26 |
+
.. automodule:: openenv.cli.commands.validate
|
| 27 |
+
:members:
|
| 28 |
+
:undoc-members:
|
| 29 |
+
:show-inheritance:
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
## `openenv push`
|
| 33 |
+
|
| 34 |
+
```{eval-rst}
|
| 35 |
+
.. automodule:: openenv.cli.commands.push
|
| 36 |
+
:members:
|
| 37 |
+
:undoc-members:
|
| 38 |
+
:show-inheritance:
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## `openenv serve`
|
| 42 |
+
|
| 43 |
+
```{eval-rst}
|
| 44 |
+
.. automodule:: openenv.cli.commands.serve
|
| 45 |
+
:members:
|
| 46 |
+
:undoc-members:
|
| 47 |
+
:show-inheritance:
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
## `openenv fork`
|
| 51 |
+
|
| 52 |
+
```{eval-rst}
|
| 53 |
+
.. automodule:: openenv.cli.commands.fork
|
| 54 |
+
:members:
|
| 55 |
+
:undoc-members:
|
| 56 |
+
:show-inheritance:
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
# API Reference
|
| 60 |
+
|
| 61 |
+
## Entry point
|
| 62 |
+
|
| 63 |
+
```{eval-rst}
|
| 64 |
+
.. automodule:: openenv.cli.__main__
|
| 65 |
+
:members:
|
| 66 |
+
:undoc-members:
|
| 67 |
+
:show-inheritance:
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
## CLI helpers
|
| 71 |
+
|
| 72 |
+
```{eval-rst}
|
| 73 |
+
.. automodule:: openenv.cli._cli_utils
|
| 74 |
+
:members:
|
| 75 |
+
:undoc-members:
|
| 76 |
+
:show-inheritance:
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
## Validation utilities
|
| 80 |
+
|
| 81 |
+
```{eval-rst}
|
| 82 |
+
.. automodule:: openenv.cli._validation
|
| 83 |
+
:members:
|
| 84 |
+
:undoc-members:
|
| 85 |
+
:show-inheritance:
|
| 86 |
+
```
|
OpenEnv/docs/source/conf.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Configuration file for the Sphinx documentation builder.
|
| 2 |
+
#
|
| 3 |
+
# For the full list of built-in configuration values, see the documentation:
|
| 4 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import sys
|
| 8 |
+
|
| 9 |
+
# -- Project information -----------------------------------------------------
|
| 10 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
| 11 |
+
|
| 12 |
+
project = "OpenEnv"
|
| 13 |
+
copyright = ""
|
| 14 |
+
author = ""
|
| 15 |
+
|
| 16 |
+
# -- Version configuration ---------------------------------------------------
|
| 17 |
+
# RELEASE env var controls stable vs dev builds (set by `make html-stable`)
|
| 18 |
+
RELEASE = os.environ.get("RELEASE", False)
|
| 19 |
+
|
| 20 |
+
# Read version from pyproject.toml
|
| 21 |
+
import tomli
|
| 22 |
+
|
| 23 |
+
pyproject_path = os.path.join(os.path.dirname(__file__), "..", "..", "pyproject.toml")
|
| 24 |
+
with open(pyproject_path, "rb") as f:
|
| 25 |
+
pyproject_data = tomli.load(f)
|
| 26 |
+
openenv_version = pyproject_data["project"]["version"]
|
| 27 |
+
|
| 28 |
+
if RELEASE:
|
| 29 |
+
version = ".".join(openenv_version.split(".")[:2])
|
| 30 |
+
release = version
|
| 31 |
+
html_title = f"OpenEnv {version} documentation"
|
| 32 |
+
switcher_version = version
|
| 33 |
+
else:
|
| 34 |
+
version = "main"
|
| 35 |
+
release = "main"
|
| 36 |
+
html_title = "OpenEnv"
|
| 37 |
+
switcher_version = "main"
|
| 38 |
+
|
| 39 |
+
# -- Path setup --------------------------------------------------------------
|
| 40 |
+
sys.path.insert(0, os.path.abspath("../../src"))
|
| 41 |
+
|
| 42 |
+
# -- General configuration ---------------------------------------------------
|
| 43 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
| 44 |
+
|
| 45 |
+
extensions = [
|
| 46 |
+
"sphinx_design",
|
| 47 |
+
"sphinx_sitemap",
|
| 48 |
+
"sphinxcontrib.mermaid",
|
| 49 |
+
"pytorch_sphinx_theme2",
|
| 50 |
+
"sphinxext.opengraph",
|
| 51 |
+
"myst_parser",
|
| 52 |
+
"sphinx.ext.autodoc",
|
| 53 |
+
"sphinx.ext.autosummary",
|
| 54 |
+
"sphinx_gallery.gen_gallery",
|
| 55 |
+
]
|
| 56 |
+
|
| 57 |
+
# -- sphinx-gallery configuration --------------------------------------------
|
| 58 |
+
from sphinx_gallery.sorting import FileNameSortKey
|
| 59 |
+
|
| 60 |
+
sphinx_gallery_conf = {
|
| 61 |
+
"examples_dirs": ["getting_started"],
|
| 62 |
+
"gallery_dirs": ["auto_getting_started"],
|
| 63 |
+
"filename_pattern": r"/plot_",
|
| 64 |
+
"ignore_pattern": r"__init__\.py",
|
| 65 |
+
"download_all_examples": False,
|
| 66 |
+
"show_memory": False,
|
| 67 |
+
"capture_repr": ("_repr_html_", "__repr__"),
|
| 68 |
+
"matplotlib_animations": True,
|
| 69 |
+
"remove_config_comments": True,
|
| 70 |
+
"within_subsection_order": FileNameSortKey,
|
| 71 |
+
"default_thumb_file": None,
|
| 72 |
+
"nested_sections": False,
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
exclude_patterns = ["getting_started/*.md", "getting_started/README.rst"]
|
| 76 |
+
|
| 77 |
+
# -- Options for HTML output -------------------------------------------------
|
| 78 |
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
| 79 |
+
|
| 80 |
+
import pytorch_sphinx_theme2
|
| 81 |
+
|
| 82 |
+
html_theme = "pytorch_sphinx_theme2"
|
| 83 |
+
html_theme_path = [pytorch_sphinx_theme2.get_html_theme_path()]
|
| 84 |
+
html_static_path = ["_static"]
|
| 85 |
+
|
| 86 |
+
html_theme_options = {
|
| 87 |
+
"navigation_with_keys": False,
|
| 88 |
+
"analytics_id": "GTM-NPLPKN5G",
|
| 89 |
+
"header_links_before_dropdown": 7,
|
| 90 |
+
"logo": {
|
| 91 |
+
"text": "OpenEnv",
|
| 92 |
+
},
|
| 93 |
+
"icon_links": [
|
| 94 |
+
{
|
| 95 |
+
"name": "X",
|
| 96 |
+
"url": "https://x.com/PyTorch",
|
| 97 |
+
"icon": "fa-brands fa-x-twitter",
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"name": "GitHub",
|
| 101 |
+
"url": "https://github.com/meta-pytorch/OpenEnv",
|
| 102 |
+
"icon": "fa-brands fa-github",
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"name": "Discourse",
|
| 106 |
+
"url": "https://dev-discuss.pytorch.org/",
|
| 107 |
+
"icon": "fa-brands fa-discourse",
|
| 108 |
+
},
|
| 109 |
+
],
|
| 110 |
+
"use_edit_page_button": True,
|
| 111 |
+
"navbar_center": "navbar-nav",
|
| 112 |
+
"switcher": {
|
| 113 |
+
"json_url": "_static/versions.json",
|
| 114 |
+
"version_match": switcher_version,
|
| 115 |
+
},
|
| 116 |
+
"check_switcher": False,
|
| 117 |
+
"navbar_align": "left",
|
| 118 |
+
"navbar_start": ["navbar-logo", "version-switcher"],
|
| 119 |
+
"navbar_center": ["navbar-nav"],
|
| 120 |
+
"navbar_end": ["theme-switcher", "navbar-icon-links"],
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
theme_variables = pytorch_sphinx_theme2.get_theme_variables()
|
| 124 |
+
|
| 125 |
+
# Templates path - local templates override theme templates
|
| 126 |
+
templates_path = [
|
| 127 |
+
"_templates",
|
| 128 |
+
os.path.join(os.path.dirname(pytorch_sphinx_theme2.__file__), "templates"),
|
| 129 |
+
]
|
| 130 |
+
|
| 131 |
+
html_context = {
|
| 132 |
+
"theme_variables": theme_variables,
|
| 133 |
+
"display_github": True,
|
| 134 |
+
"github_url": "https://github.com",
|
| 135 |
+
"github_user": "meta-pytorch",
|
| 136 |
+
"github_repo": "OpenEnv",
|
| 137 |
+
"feedback_url": "https://github.com/meta-pytorch/OpenEnv",
|
| 138 |
+
"github_version": "main",
|
| 139 |
+
"doc_path": "docs/source",
|
| 140 |
+
"library_links": theme_variables.get("library_links", []),
|
| 141 |
+
"community_links": theme_variables.get("community_links", []),
|
| 142 |
+
"language_bindings_links": html_theme_options.get("language_bindings_links", []),
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
# Base URL for the site (used by sitemap and canonical URLs)
|
| 146 |
+
html_baseurl = "https://meta-pytorch.org/OpenEnv/"
|
| 147 |
+
sitemap_locales = [None]
|
| 148 |
+
sitemap_excludes = [
|
| 149 |
+
"search.html",
|
| 150 |
+
"genindex.html",
|
| 151 |
+
]
|
| 152 |
+
sitemap_url_scheme = "{link}"
|
| 153 |
+
|
| 154 |
+
# -- MyST-Parser configuration -----------------------------------------------
|
| 155 |
+
myst_enable_extensions = [
|
| 156 |
+
"colon_fence",
|
| 157 |
+
"deflist",
|
| 158 |
+
"html_image",
|
| 159 |
+
]
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
# -- Post-process sphinx-gallery output to fix navigation --------------------
|
| 163 |
+
def remove_orphan_and_duplicate_toctree(app, docname, source):
|
| 164 |
+
"""Remove :orphan: and duplicate hidden toctree from gallery index."""
|
| 165 |
+
if docname == "auto_getting_started/index":
|
| 166 |
+
content = source[0]
|
| 167 |
+
# Remove the :orphan: directive
|
| 168 |
+
if content.startswith(":orphan:"):
|
| 169 |
+
content = content.replace(":orphan:\n\n", "", 1)
|
| 170 |
+
content = content.replace(":orphan:\n", "", 1)
|
| 171 |
+
|
| 172 |
+
# Remove the sphinx-gallery generated hidden toctree
|
| 173 |
+
# Find and remove the hidden toctree block
|
| 174 |
+
import re
|
| 175 |
+
|
| 176 |
+
# Match: .. toctree::\n :hidden:\n\n /auto_getting_started/...
|
| 177 |
+
pattern = r"\.\. toctree::\n\s+:hidden:\n\n(?:\s+/auto_getting_started/plot_\d+_\w+\n)+"
|
| 178 |
+
content = re.sub(pattern, "", content)
|
| 179 |
+
|
| 180 |
+
source[0] = content
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def copy_md_pages_to_gallery(app):
|
| 184 |
+
"""Copy .md pages from getting_started/ to auto_getting_started/.
|
| 185 |
+
|
| 186 |
+
Sphinx Gallery only processes .py files and README.rst. Any extra .md
|
| 187 |
+
pages that live alongside the gallery source must be copied into the
|
| 188 |
+
generated gallery directory so Sphinx can discover them as part of the
|
| 189 |
+
same toctree (important for section-nav context in pydata-sphinx-theme).
|
| 190 |
+
"""
|
| 191 |
+
import glob
|
| 192 |
+
import shutil
|
| 193 |
+
|
| 194 |
+
srcdir = os.path.join(app.srcdir, "getting_started")
|
| 195 |
+
dstdir = os.path.join(app.srcdir, "auto_getting_started")
|
| 196 |
+
os.makedirs(dstdir, exist_ok=True)
|
| 197 |
+
for md_file in glob.glob(os.path.join(srcdir, "*.md")):
|
| 198 |
+
shutil.copy2(md_file, dstdir)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def setup(app):
|
| 202 |
+
# Copy extra .md pages into the gallery output dir (priority 900 so it
|
| 203 |
+
# runs after sphinx-gallery's builder-inited handler at default priority).
|
| 204 |
+
app.connect("builder-inited", copy_md_pages_to_gallery, priority=900)
|
| 205 |
+
# Hook into source-read to modify content before Sphinx processes it
|
| 206 |
+
app.connect("source-read", remove_orphan_and_duplicate_toctree)
|
OpenEnv/docs/source/core.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Core API
|
| 2 |
+
|
| 3 |
+
The `openenv.core` package provides the core abstractions for building and running environments. For an end-to-end tutorial on building environments with OpenEnv, see the [building an environment](auto_getting_started/environment-builder.md) guide.
|
| 4 |
+
|
| 5 |
+
## Server
|
| 6 |
+
|
| 7 |
+
### Environment server primitives
|
| 8 |
+
|
| 9 |
+
```{eval-rst}
|
| 10 |
+
.. automodule:: openenv.core.env_server.interfaces
|
| 11 |
+
:members:
|
| 12 |
+
:undoc-members:
|
| 13 |
+
:show-inheritance:
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
### Types
|
| 17 |
+
|
| 18 |
+
```{eval-rst}
|
| 19 |
+
.. automodule:: openenv.core.env_server.types
|
| 20 |
+
:members:
|
| 21 |
+
:undoc-members:
|
| 22 |
+
:show-inheritance:
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
### Exceptions
|
| 26 |
+
|
| 27 |
+
```{eval-rst}
|
| 28 |
+
.. automodule:: openenv.core.env_server.exceptions
|
| 29 |
+
:members:
|
| 30 |
+
:undoc-members:
|
| 31 |
+
:show-inheritance:
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
### HTTP server utilities
|
| 35 |
+
|
| 36 |
+
```{eval-rst}
|
| 37 |
+
.. automodule:: openenv.core.env_server.http_server
|
| 38 |
+
:members:
|
| 39 |
+
:undoc-members:
|
| 40 |
+
:show-inheritance:
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
### Web interface helpers
|
| 44 |
+
|
| 45 |
+
```{eval-rst}
|
| 46 |
+
.. automodule:: openenv.core.env_server.web_interface
|
| 47 |
+
:members:
|
| 48 |
+
:undoc-members:
|
| 49 |
+
:show-inheritance:
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
### Serialization
|
| 53 |
+
|
| 54 |
+
```{eval-rst}
|
| 55 |
+
.. automodule:: openenv.core.env_server.serialization
|
| 56 |
+
:members:
|
| 57 |
+
:undoc-members:
|
| 58 |
+
:show-inheritance:
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
### Transforms
|
| 62 |
+
|
| 63 |
+
```{eval-rst}
|
| 64 |
+
.. automodule:: openenv.core.env_server.base_transforms
|
| 65 |
+
:members:
|
| 66 |
+
:undoc-members:
|
| 67 |
+
:show-inheritance:
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
### Route configuration
|
| 71 |
+
|
| 72 |
+
```{eval-rst}
|
| 73 |
+
.. automodule:: openenv.core.env_server.route_config
|
| 74 |
+
:members:
|
| 75 |
+
:undoc-members:
|
| 76 |
+
:show-inheritance:
|
| 77 |
+
```
|
| 78 |
+
|
| 79 |
+
## Clients
|
| 80 |
+
|
| 81 |
+
### Base client
|
| 82 |
+
|
| 83 |
+
```{eval-rst}
|
| 84 |
+
.. automodule:: openenv.core.env_client
|
| 85 |
+
:members:
|
| 86 |
+
:undoc-members:
|
| 87 |
+
:show-inheritance:
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
### Synchronous client
|
| 91 |
+
|
| 92 |
+
```{eval-rst}
|
| 93 |
+
.. automodule:: openenv.core.sync_client
|
| 94 |
+
:members:
|
| 95 |
+
:undoc-members:
|
| 96 |
+
:show-inheritance:
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
### Generic client
|
| 100 |
+
|
| 101 |
+
```{eval-rst}
|
| 102 |
+
.. automodule:: openenv.core.generic_client
|
| 103 |
+
:members:
|
| 104 |
+
:undoc-members:
|
| 105 |
+
:show-inheritance:
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
### LLM client
|
| 109 |
+
|
| 110 |
+
```{eval-rst}
|
| 111 |
+
.. automodule:: openenv.core.llm_client
|
| 112 |
+
:members:
|
| 113 |
+
:undoc-members:
|
| 114 |
+
:show-inheritance:
|
| 115 |
+
```
|
| 116 |
+
|
| 117 |
+
### Shared dataclasses
|
| 118 |
+
|
| 119 |
+
```{eval-rst}
|
| 120 |
+
.. automodule:: openenv.core.client_types
|
| 121 |
+
:members:
|
| 122 |
+
:undoc-members:
|
| 123 |
+
:show-inheritance:
|
| 124 |
+
```
|
| 125 |
+
|
| 126 |
+
## MCP (Model Context Protocol)
|
| 127 |
+
|
| 128 |
+
### MCP environment
|
| 129 |
+
|
| 130 |
+
```{eval-rst}
|
| 131 |
+
.. automodule:: openenv.core.env_server.mcp_environment
|
| 132 |
+
:members:
|
| 133 |
+
:undoc-members:
|
| 134 |
+
:show-inheritance:
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
### MCP types
|
| 138 |
+
|
| 139 |
+
```{eval-rst}
|
| 140 |
+
.. automodule:: openenv.core.env_server.mcp_types
|
| 141 |
+
:members:
|
| 142 |
+
:undoc-members:
|
| 143 |
+
:show-inheritance:
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
### MCP client
|
| 147 |
+
|
| 148 |
+
```{eval-rst}
|
| 149 |
+
.. automodule:: openenv.core.mcp_client
|
| 150 |
+
:members:
|
| 151 |
+
:undoc-members:
|
| 152 |
+
:show-inheritance:
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
## Rubrics
|
| 156 |
+
|
| 157 |
+
```{eval-rst}
|
| 158 |
+
.. automodule:: openenv.core.rubrics.base
|
| 159 |
+
:members:
|
| 160 |
+
:undoc-members:
|
| 161 |
+
:show-inheritance:
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
```{eval-rst}
|
| 165 |
+
.. automodule:: openenv.core.rubrics.containers
|
| 166 |
+
:members:
|
| 167 |
+
:undoc-members:
|
| 168 |
+
:show-inheritance:
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
```{eval-rst}
|
| 172 |
+
.. automodule:: openenv.core.rubrics.trajectory
|
| 173 |
+
:members:
|
| 174 |
+
:undoc-members:
|
| 175 |
+
:show-inheritance:
|
| 176 |
+
```
|
| 177 |
+
|
| 178 |
+
```{eval-rst}
|
| 179 |
+
.. automodule:: openenv.core.rubrics.llm_judge
|
| 180 |
+
:members:
|
| 181 |
+
:undoc-members:
|
| 182 |
+
:show-inheritance:
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
## Tools
|
| 186 |
+
|
| 187 |
+
```{eval-rst}
|
| 188 |
+
.. automodule:: openenv.core.tools.git_server_client
|
| 189 |
+
:members:
|
| 190 |
+
:undoc-members:
|
| 191 |
+
:show-inheritance:
|
| 192 |
+
```
|
| 193 |
+
|
| 194 |
+
```{eval-rst}
|
| 195 |
+
.. automodule:: openenv.core.tools.local_python_executor
|
| 196 |
+
:members:
|
| 197 |
+
:undoc-members:
|
| 198 |
+
:show-inheritance:
|
| 199 |
+
```
|
| 200 |
+
|
| 201 |
+
## Container providers
|
| 202 |
+
|
| 203 |
+
```{eval-rst}
|
| 204 |
+
.. automodule:: openenv.core.containers.runtime.providers
|
| 205 |
+
:members:
|
| 206 |
+
:undoc-members:
|
| 207 |
+
:show-inheritance:
|
| 208 |
+
```
|
| 209 |
+
|
| 210 |
+
```{eval-rst}
|
| 211 |
+
.. automodule:: openenv.core.containers.runtime.uv_provider
|
| 212 |
+
:members:
|
| 213 |
+
:undoc-members:
|
| 214 |
+
:show-inheritance:
|
| 215 |
+
```
|
OpenEnv/docs/source/customizing-web-ui.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Custom Web UI
|
| 2 |
+
|
| 3 |
+
When `ENABLE_WEB_INTERFACE=true`, the server serves a default Gradio app at `/web` with Reset/Step/Get state, Quick Start, and README. Environment authors can **add** a custom tab by providing a custom Gradio builder.
|
| 4 |
+
|
| 5 |
+
## Extension point: `gradio_builder`
|
| 6 |
+
|
| 7 |
+
`create_app()` accepts an optional **`gradio_builder`** callable. When set, the UI at `/web` is built with [Gradioโs TabbedInterface](https://www.gradio.app/4.44.1/docs/gradio/tabbedinterface): the **first tab (โPlaygroundโ)** is the default OpenEnv UI, and the **second tab (โCustomโ)** is the `gr.Blocks` returned by your builder. Users can switch between the default Playground and your custom interface without losing either. The same `/web/reset`, `/web/step`, `/web/state`, and `/web/metadata` API routes remain available; your custom tab can use the provided `web_manager` in-process or call those endpoints.
|
| 8 |
+
|
| 9 |
+
### Builder signature
|
| 10 |
+
|
| 11 |
+
```python
|
| 12 |
+
def my_gradio_builder(
|
| 13 |
+
web_manager, # WebInterfaceManager: .reset_environment(), .step_environment(), .get_state()
|
| 14 |
+
action_fields, # list[dict]: from action schema for form generation
|
| 15 |
+
metadata, # EnvironmentMetadata | None: name, readme_content, etc.
|
| 16 |
+
is_chat_env, # bool: True if single message input
|
| 17 |
+
title, # str: app title (e.g. metadata.name)
|
| 18 |
+
quick_start_md, # str: Quick Start markdown (class names already replaced)
|
| 19 |
+
) -> gr.Blocks:
|
| 20 |
+
...
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
Return a `gr.Blocks` instance. It is shown in the **โCustomโ** tab of a tabbed interface; the **โPlaygroundโ** tab always shows the default OpenEnv UI. Core applies the same theme/css when mounting.
|
| 24 |
+
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
## Option 1: Add a custom tab
|
| 28 |
+
|
| 29 |
+
Provide a builder that returns your own `gr.Blocks`; it appears as the second tab (โCustomโ) next to the default โPlaygroundโ tab:
|
| 30 |
+
|
| 31 |
+
```python
|
| 32 |
+
# server/app.py
|
| 33 |
+
from openenv.core.env_server.http_server import create_app
|
| 34 |
+
from .my_environment import MyEnvironment
|
| 35 |
+
from ..models import MyAction, MyObservation
|
| 36 |
+
from .gradio_ui import build_my_gradio_app # your module
|
| 37 |
+
|
| 38 |
+
app = create_app(
|
| 39 |
+
MyEnvironment,
|
| 40 |
+
MyAction,
|
| 41 |
+
MyObservation,
|
| 42 |
+
env_name="my_env",
|
| 43 |
+
gradio_builder=build_my_gradio_app,
|
| 44 |
+
)
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
In `server/gradio_ui.py` implement `build_my_gradio_app(web_manager, action_fields, metadata, is_chat_env, title, quick_start_md)` returning a `gr.Blocks` (e.g. env-specific visualizations, extra controls). Use `web_manager.reset_environment()`, `web_manager.step_environment(action_data)`, and `web_manager.get_state()` in your Gradio event handlers. The default Playground tab remains available in the first tab.
|
| 48 |
+
|
| 49 |
+
---
|
| 50 |
+
|
| 51 |
+
## Option 2: Custom tab that wraps or reuses the default
|
| 52 |
+
|
| 53 |
+
Your builder can call the core `build_gradio_app` to get a Blocks instance and embed it inside your custom tab (e.g. in a `gr.Tabs` or as one section). That way your โCustomโ tab can show both the default layout and additional content in one place.
|
| 54 |
+
|
| 55 |
+
---
|
| 56 |
+
|
| 57 |
+
## Option 3: Custom Quick Start or README only
|
| 58 |
+
|
| 59 |
+
You donโt need a custom builder only to change text. The default UI uses:
|
| 60 |
+
|
| 61 |
+
- **Quick Start**: generated from `get_quick_start_markdown(metadata, action_cls, observation_cls)` (init-style class names).
|
| 62 |
+
- **README**: `metadata.readme_content` (loaded from the envโs README).
|
| 63 |
+
|
| 64 |
+
So you can influence the default UI by ensuring `metadata` and README are correct. To change the Quick Start template itself (e.g. different wording or placeholders), you would use a custom `gradio_builder` that calls `build_gradio_app` with a custom `quick_start_md` string you build yourself (or by copying and adapting the default template from the core).
|
| 65 |
+
|
| 66 |
+
---
|
| 67 |
+
|
| 68 |
+
## Migration from custom HTML override (e.g. wildfire)
|
| 69 |
+
|
| 70 |
+
Environments that currently override `/web` with custom HTML (e.g. by removing the default route and adding a GET `/web` that returns HTML) should migrate to a **gradio_builder** that returns a `gr.Blocks` app. The custom UI then appears in the **โCustomโ** tab alongside the default **โPlaygroundโ** tab. Benefits:
|
| 71 |
+
|
| 72 |
+
- Single, supported extension point using [TabbedInterface](https://www.gradio.app/4.44.1/docs/gradio/tabbedinterface).
|
| 73 |
+
- No need to remove or override routes; the default UI stays in the first tab.
|
| 74 |
+
- Same `/web` path; both tabs can use `web_manager` or `/web/reset`, `/web/step`, `/web/state`.
|
| 75 |
+
|
| 76 |
+
If you need a non-Gradio custom UI (e.g. static HTML/JS), you can still register your own route after `create_app` (e.g. at `/web/custom` or another path), but the main `/web` slot is the Gradio tabbed app when `ENABLE_WEB_INTERFACE=true`.
|
| 77 |
+
|
| 78 |
+
---
|
| 79 |
+
|
| 80 |
+
## Summary
|
| 81 |
+
|
| 82 |
+
| Goal | Approach |
|
| 83 |
+
|-----------------------------|---------------------------------------------------------------------------|
|
| 84 |
+
| Use default UI only | Do not pass `gradio_builder`. |
|
| 85 |
+
| Add a custom tab | Pass `gradio_builder=my_builder`; return your own `gr.Blocks` (shown in โCustomโ tab). |
|
| 86 |
+
| Custom tab + default inside | In your builder, call `build_gradio_app(...)` and embed or wrap it in your Blocks. |
|
| 87 |
+
| Change Quick Start / README | Rely on metadata/README, or custom builder that builds custom markdown. |
|
| 88 |
+
|
| 89 |
+
The default Playground tab is built with `openenv.core.env_server.gradio_ui.build_gradio_app`; you can import and call it with the same arguments if your custom tab needs to embed or extend it.
|
OpenEnv/docs/source/environments.md
ADDED
|
@@ -0,0 +1,552 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Environments
|
| 2 |
+
|
| 3 |
+
The OpenEnv community has built a catalog of ready-to-run environments that cover deterministic smoke tests, full developer workflows, and multi-step reasoning challenges. Explore the surface area below and jump directly into the guides for each environment.
|
| 4 |
+
|
| 5 |
+
`````{grid} 1 2 3 3
|
| 6 |
+
:gutter: 3
|
| 7 |
+
|
| 8 |
+
````{grid-item-card} Echo
|
| 9 |
+
:class-card: sd-border-1
|
| 10 |
+
|
| 11 |
+
Minimal observation/action loop for verifying client integrations, CI pipelines, and onboarding flows in seconds.
|
| 12 |
+
|
| 13 |
+
+++
|
| 14 |
+
```{button-link} environments/echo.html
|
| 15 |
+
:color: primary
|
| 16 |
+
:outline:
|
| 17 |
+
|
| 18 |
+
{octicon}`file;1em` Docs
|
| 19 |
+
```
|
| 20 |
+
```{button-link} https://huggingface.co/spaces/openenv/echo_env
|
| 21 |
+
:color: warning
|
| 22 |
+
:outline:
|
| 23 |
+
|
| 24 |
+
๐ค Hugging Face
|
| 25 |
+
```
|
| 26 |
+
````
|
| 27 |
+
|
| 28 |
+
````{grid-item-card} Coding
|
| 29 |
+
:class-card: sd-border-1
|
| 30 |
+
|
| 31 |
+
Secure sandbox with filesystem access and evaluation hooks for executing generated code and building autonomous dev workflows.
|
| 32 |
+
|
| 33 |
+
+++
|
| 34 |
+
```{button-link} environments/coding.html
|
| 35 |
+
:color: primary
|
| 36 |
+
:outline:
|
| 37 |
+
|
| 38 |
+
{octicon}`file;1em` Docs
|
| 39 |
+
```
|
| 40 |
+
```{button-link} https://huggingface.co/spaces/openenv/coding_env
|
| 41 |
+
:color: warning
|
| 42 |
+
:outline:
|
| 43 |
+
|
| 44 |
+
๐ค Hugging Face
|
| 45 |
+
```
|
| 46 |
+
````
|
| 47 |
+
|
| 48 |
+
````{grid-item-card} Chat
|
| 49 |
+
:class-card: sd-border-1
|
| 50 |
+
|
| 51 |
+
Message-driven loop tailored for conversational agents that need structured turns, safety rails, and message attribution.
|
| 52 |
+
|
| 53 |
+
+++
|
| 54 |
+
```{button-link} environments/chat.html
|
| 55 |
+
:color: primary
|
| 56 |
+
:outline:
|
| 57 |
+
|
| 58 |
+
{octicon}`file;1em` Docs
|
| 59 |
+
```
|
| 60 |
+
```{button-link} https://huggingface.co/spaces/openenv/chat_env
|
| 61 |
+
:color: warning
|
| 62 |
+
:outline:
|
| 63 |
+
|
| 64 |
+
๐ค Hugging Face
|
| 65 |
+
```
|
| 66 |
+
````
|
| 67 |
+
|
| 68 |
+
````{grid-item-card} Atari
|
| 69 |
+
:class-card: sd-border-1
|
| 70 |
+
|
| 71 |
+
Classic Arcade Learning Environment tasks packaged for fast benchmarking of reinforcement-learning style agents.
|
| 72 |
+
|
| 73 |
+
+++
|
| 74 |
+
```{button-link} environments/atari.html
|
| 75 |
+
:color: primary
|
| 76 |
+
:outline:
|
| 77 |
+
|
| 78 |
+
{octicon}`file;1em` Docs
|
| 79 |
+
```
|
| 80 |
+
```{button-link} https://huggingface.co/spaces/openenv/atari_env
|
| 81 |
+
:color: warning
|
| 82 |
+
:outline:
|
| 83 |
+
|
| 84 |
+
๐ค Hugging Face
|
| 85 |
+
```
|
| 86 |
+
````
|
| 87 |
+
|
| 88 |
+
````{grid-item-card} OpenSpiel
|
| 89 |
+
:class-card: sd-border-1
|
| 90 |
+
|
| 91 |
+
Multi-agent, game-theory workloads powered by DeepMind's OpenSpiel suite, ideal for search and self-play experiments.
|
| 92 |
+
|
| 93 |
+
+++
|
| 94 |
+
```{button-link} environments/openspiel.html
|
| 95 |
+
:color: primary
|
| 96 |
+
:outline:
|
| 97 |
+
|
| 98 |
+
{octicon}`file;1em` Docs
|
| 99 |
+
```
|
| 100 |
+
```{button-link} https://huggingface.co/spaces/openenv/openspiel_env
|
| 101 |
+
:color: warning
|
| 102 |
+
:outline:
|
| 103 |
+
|
| 104 |
+
๐ค Hugging Face
|
| 105 |
+
```
|
| 106 |
+
````
|
| 107 |
+
|
| 108 |
+
````{grid-item-card} SUMO-RL
|
| 109 |
+
:class-card: sd-border-1
|
| 110 |
+
|
| 111 |
+
Traffic control scenarios with SUMO simulators for agents that reason about continuous control and scheduling.
|
| 112 |
+
|
| 113 |
+
+++
|
| 114 |
+
```{button-link} environments/sumo.html
|
| 115 |
+
:color: primary
|
| 116 |
+
:outline:
|
| 117 |
+
|
| 118 |
+
{octicon}`file;1em` Docs
|
| 119 |
+
```
|
| 120 |
+
````
|
| 121 |
+
|
| 122 |
+
````{grid-item-card} FinRL
|
| 123 |
+
:class-card: sd-border-1
|
| 124 |
+
|
| 125 |
+
Financial market simulations with portfolio APIs, perfect for RLHF strategies and algorithmic trading experiments.
|
| 126 |
+
|
| 127 |
+
+++
|
| 128 |
+
```{button-link} environments/finrl.html
|
| 129 |
+
:color: primary
|
| 130 |
+
:outline:
|
| 131 |
+
|
| 132 |
+
{octicon}`file;1em` Docs
|
| 133 |
+
```
|
| 134 |
+
````
|
| 135 |
+
|
| 136 |
+
````{grid-item-card} TextArena
|
| 137 |
+
:class-card: sd-border-1
|
| 138 |
+
|
| 139 |
+
Multi-task text arena for language-game competitions such as Wordle, reasoning puzzles, and program synthesis.
|
| 140 |
+
|
| 141 |
+
+++
|
| 142 |
+
```{button-link} environments/textarena.html
|
| 143 |
+
:color: primary
|
| 144 |
+
:outline:
|
| 145 |
+
|
| 146 |
+
{octicon}`file;1em` Docs
|
| 147 |
+
```
|
| 148 |
+
```{button-link} https://huggingface.co/spaces/burtenshaw/textarena_env
|
| 149 |
+
:color: warning
|
| 150 |
+
:outline:
|
| 151 |
+
|
| 152 |
+
๐ค Hugging Face
|
| 153 |
+
```
|
| 154 |
+
````
|
| 155 |
+
|
| 156 |
+
````{grid-item-card} Git
|
| 157 |
+
:class-card: sd-border-1
|
| 158 |
+
|
| 159 |
+
Teaches agents to navigate repositories, inspect diffs, and land changes via Git-native operations.
|
| 160 |
+
|
| 161 |
+
+++
|
| 162 |
+
```{button-link} environments/git.html
|
| 163 |
+
:color: primary
|
| 164 |
+
:outline:
|
| 165 |
+
|
| 166 |
+
{octicon}`file;1em` Docs
|
| 167 |
+
```
|
| 168 |
+
````
|
| 169 |
+
|
| 170 |
+
````{grid-item-card} DIPG Safety
|
| 171 |
+
:class-card: sd-border-1
|
| 172 |
+
|
| 173 |
+
Safety-critical diagnostics from the DIPG benchmark, highlighting guardrails, adversarial prompts, and risk scoring.
|
| 174 |
+
|
| 175 |
+
+++
|
| 176 |
+
```{button-link} environments/dipg.html
|
| 177 |
+
:color: primary
|
| 178 |
+
:outline:
|
| 179 |
+
|
| 180 |
+
{octicon}`file;1em` Docs
|
| 181 |
+
```
|
| 182 |
+
```{button-link} https://huggingface.co/spaces/surfiniaburger/dipg-gym
|
| 183 |
+
:color: warning
|
| 184 |
+
:outline:
|
| 185 |
+
|
| 186 |
+
๐ค Hugging Face
|
| 187 |
+
```
|
| 188 |
+
````
|
| 189 |
+
|
| 190 |
+
````{grid-item-card} Snake
|
| 191 |
+
:class-card: sd-border-1
|
| 192 |
+
|
| 193 |
+
Classic snake game environment for RL research with configurable grids, partial observability, and customizable rewards.
|
| 194 |
+
|
| 195 |
+
+++
|
| 196 |
+
```{button-link} environments/snake.html
|
| 197 |
+
:color: primary
|
| 198 |
+
:outline:
|
| 199 |
+
|
| 200 |
+
{octicon}`file;1em` Docs
|
| 201 |
+
```
|
| 202 |
+
```{button-link} https://huggingface.co/spaces/Crashbandicoote2/snake_env
|
| 203 |
+
:color: warning
|
| 204 |
+
:outline:
|
| 205 |
+
|
| 206 |
+
๐ค Hugging Face
|
| 207 |
+
```
|
| 208 |
+
````
|
| 209 |
+
|
| 210 |
+
````{grid-item-card} Web Search
|
| 211 |
+
:class-card: sd-border-1
|
| 212 |
+
|
| 213 |
+
Web search environment for RL research with configurable grids, partial observability, and customizable rewards.
|
| 214 |
+
|
| 215 |
+
+++
|
| 216 |
+
```{button-link} environments/websearch.html
|
| 217 |
+
:color: primary
|
| 218 |
+
:outline:
|
| 219 |
+
|
| 220 |
+
{octicon}`file;1em` Docs
|
| 221 |
+
```
|
| 222 |
+
```{button-link} https://huggingface.co/spaces/lawhy/web_search
|
| 223 |
+
:color: warning
|
| 224 |
+
:outline:
|
| 225 |
+
|
| 226 |
+
๐ค Hugging Face
|
| 227 |
+
```
|
| 228 |
+
````
|
| 229 |
+
|
| 230 |
+
````{grid-item-card} BrowserGym
|
| 231 |
+
:class-card: sd-border-1
|
| 232 |
+
|
| 233 |
+
Browser automation environment for web agents with DOM interaction, navigation, and multi-step task completion.
|
| 234 |
+
|
| 235 |
+
+++
|
| 236 |
+
```{button-link} environments/browsergym.html
|
| 237 |
+
:color: primary
|
| 238 |
+
:outline:
|
| 239 |
+
|
| 240 |
+
{octicon}`file;1em` Docs
|
| 241 |
+
```
|
| 242 |
+
```{button-link} https://huggingface.co/spaces/burtenshaw/browsergym-v2
|
| 243 |
+
:color: warning
|
| 244 |
+
:outline:
|
| 245 |
+
|
| 246 |
+
๐ค Hugging Face
|
| 247 |
+
```
|
| 248 |
+
````
|
| 249 |
+
|
| 250 |
+
````{grid-item-card} KernRL
|
| 251 |
+
:class-card: sd-border-1
|
| 252 |
+
|
| 253 |
+
RL environment for GPU kernel optimization. Train LLM agents to write fast CUDA/Triton kernels that beat baseline implementations.
|
| 254 |
+
|
| 255 |
+
+++
|
| 256 |
+
```{button-link} environments/kernrl.html
|
| 257 |
+
:color: primary
|
| 258 |
+
:outline:
|
| 259 |
+
|
| 260 |
+
{octicon}`file;1em` Docs
|
| 261 |
+
```
|
| 262 |
+
````
|
| 263 |
+
|
| 264 |
+
````{grid-item-card} Calendar
|
| 265 |
+
:class-card: sd-border-1
|
| 266 |
+
|
| 267 |
+
Calendar tool-use environment exposing a Calendar Gym through the OpenEnv reset/step/state interface for scheduling agents.
|
| 268 |
+
|
| 269 |
+
+++
|
| 270 |
+
```{button-link} environments/calendar.html
|
| 271 |
+
:color: primary
|
| 272 |
+
:outline:
|
| 273 |
+
|
| 274 |
+
{octicon}`file;1em` Docs
|
| 275 |
+
```
|
| 276 |
+
````
|
| 277 |
+
|
| 278 |
+
````{grid-item-card} CARLA
|
| 279 |
+
:class-card: sd-border-1
|
| 280 |
+
|
| 281 |
+
Embodied evaluation environment for testing LLM decision-making in a full 3D driving simulator with irreversible consequences and ethical trolley scenarios.
|
| 282 |
+
|
| 283 |
+
+++
|
| 284 |
+
```{button-link} environments/carla.html
|
| 285 |
+
:color: primary
|
| 286 |
+
:outline:
|
| 287 |
+
|
| 288 |
+
{octicon}`file;1em` Docs
|
| 289 |
+
```
|
| 290 |
+
```{button-link} https://huggingface.co/spaces/sergiopaniego/carla-env
|
| 291 |
+
:color: warning
|
| 292 |
+
:outline:
|
| 293 |
+
|
| 294 |
+
๐ค Hugging Face
|
| 295 |
+
```
|
| 296 |
+
````
|
| 297 |
+
|
| 298 |
+
````{grid-item-card} Chess
|
| 299 |
+
:class-card: sd-border-1
|
| 300 |
+
|
| 301 |
+
Chess RL environment powered by the moonfish engine with configurable opponents, position evaluation, and full chess rules.
|
| 302 |
+
|
| 303 |
+
+++
|
| 304 |
+
```{button-link} environments/chess.html
|
| 305 |
+
:color: primary
|
| 306 |
+
:outline:
|
| 307 |
+
|
| 308 |
+
{octicon}`file;1em` Docs
|
| 309 |
+
```
|
| 310 |
+
````
|
| 311 |
+
|
| 312 |
+
````{grid-item-card} Connect4
|
| 313 |
+
:class-card: sd-border-1
|
| 314 |
+
|
| 315 |
+
Classic Connect Four board game environment for training agents on turn-based strategy with a 6ร7 grid.
|
| 316 |
+
|
| 317 |
+
+++
|
| 318 |
+
```{button-link} environments/connect4.html
|
| 319 |
+
:color: primary
|
| 320 |
+
:outline:
|
| 321 |
+
|
| 322 |
+
{octicon}`file;1em` Docs
|
| 323 |
+
```
|
| 324 |
+
````
|
| 325 |
+
|
| 326 |
+
````{grid-item-card} DM Control
|
| 327 |
+
:class-card: sd-border-1
|
| 328 |
+
|
| 329 |
+
Generic OpenEnv wrapper for dm_control.suite, providing access to all MuJoCo-based continuous control tasks like cartpole, walker, and humanoid.
|
| 330 |
+
|
| 331 |
+
+++
|
| 332 |
+
```{button-link} environments/dm_control.html
|
| 333 |
+
:color: primary
|
| 334 |
+
:outline:
|
| 335 |
+
|
| 336 |
+
{octicon}`file;1em` Docs
|
| 337 |
+
```
|
| 338 |
+
````
|
| 339 |
+
|
| 340 |
+
````{grid-item-card} FinQA
|
| 341 |
+
:class-card: sd-border-1
|
| 342 |
+
|
| 343 |
+
Financial question-answering environment that evaluates LLMs on complex financial questions using tool calls on SEC 10-K filing data.
|
| 344 |
+
|
| 345 |
+
+++
|
| 346 |
+
```{button-link} environments/finqa.html
|
| 347 |
+
:color: primary
|
| 348 |
+
:outline:
|
| 349 |
+
|
| 350 |
+
{octicon}`file;1em` Docs
|
| 351 |
+
```
|
| 352 |
+
````
|
| 353 |
+
|
| 354 |
+
````{grid-item-card} Grid World
|
| 355 |
+
:class-card: sd-border-1
|
| 356 |
+
|
| 357 |
+
Simple 5ร5 grid world RL testbed and step-by-step guide for building new OpenEnv environments from scratch.
|
| 358 |
+
|
| 359 |
+
+++
|
| 360 |
+
```{button-link} environments/grid_world.html
|
| 361 |
+
:color: primary
|
| 362 |
+
:outline:
|
| 363 |
+
|
| 364 |
+
{octicon}`file;1em` Docs
|
| 365 |
+
```
|
| 366 |
+
```{button-link} https://huggingface.co/spaces/yuvrajpant56/grid_world_env
|
| 367 |
+
:color: warning
|
| 368 |
+
:outline:
|
| 369 |
+
|
| 370 |
+
๐ค Hugging Face
|
| 371 |
+
```
|
| 372 |
+
````
|
| 373 |
+
|
| 374 |
+
````{grid-item-card} Julia
|
| 375 |
+
:class-card: sd-border-1
|
| 376 |
+
|
| 377 |
+
Julia code execution environment with test result tracking and reward calculation for RL training on Julia programming tasks.
|
| 378 |
+
|
| 379 |
+
+++
|
| 380 |
+
```{button-link} environments/julia.html
|
| 381 |
+
:color: primary
|
| 382 |
+
:outline:
|
| 383 |
+
|
| 384 |
+
{octicon}`file;1em` Docs
|
| 385 |
+
```
|
| 386 |
+
````
|
| 387 |
+
|
| 388 |
+
````{grid-item-card} Maze
|
| 389 |
+
:class-card: sd-border-1
|
| 390 |
+
|
| 391 |
+
Gridworld maze where agents navigate from start to exit while avoiding walls, with configurable 8ร8 layouts.
|
| 392 |
+
|
| 393 |
+
+++
|
| 394 |
+
```{button-link} environments/maze.html
|
| 395 |
+
:color: primary
|
| 396 |
+
:outline:
|
| 397 |
+
|
| 398 |
+
{octicon}`file;1em` Docs
|
| 399 |
+
```
|
| 400 |
+
````
|
| 401 |
+
|
| 402 |
+
````{grid-item-card} OpenApp
|
| 403 |
+
:class-card: sd-border-1
|
| 404 |
+
|
| 405 |
+
Web application simulation wrapping the OpenApps framework and BrowserGym for training UI agents on calendar, todo, messenger, and maps apps.
|
| 406 |
+
|
| 407 |
+
+++
|
| 408 |
+
```{button-link} environments/openapp.html
|
| 409 |
+
:color: primary
|
| 410 |
+
:outline:
|
| 411 |
+
|
| 412 |
+
{octicon}`file;1em` Docs
|
| 413 |
+
```
|
| 414 |
+
````
|
| 415 |
+
|
| 416 |
+
````{grid-item-card} Reasoning Gym
|
| 417 |
+
:class-card: sd-border-1
|
| 418 |
+
|
| 419 |
+
Integrates the Reasoning Gym library to provide single-step reasoning tasks with configurable datasets and scoring.
|
| 420 |
+
|
| 421 |
+
+++
|
| 422 |
+
```{button-link} environments/reasoning_gym.html
|
| 423 |
+
:color: primary
|
| 424 |
+
:outline:
|
| 425 |
+
|
| 426 |
+
{octicon}`file;1em` Docs
|
| 427 |
+
```
|
| 428 |
+
````
|
| 429 |
+
|
| 430 |
+
````{grid-item-card} REPL
|
| 431 |
+
:class-card: sd-border-1
|
| 432 |
+
|
| 433 |
+
Python REPL environment for code execution tasks based on the Recursive Language Models paradigm with sandboxed execution and context loading.
|
| 434 |
+
|
| 435 |
+
+++
|
| 436 |
+
```{button-link} environments/repl.html
|
| 437 |
+
:color: primary
|
| 438 |
+
:outline:
|
| 439 |
+
|
| 440 |
+
{octicon}`file;1em` Docs
|
| 441 |
+
```
|
| 442 |
+
````
|
| 443 |
+
|
| 444 |
+
````{grid-item-card} TB2
|
| 445 |
+
:class-card: sd-border-1
|
| 446 |
+
|
| 447 |
+
OpenEnv wrapper for Terminal-Bench 2 tasks with local and Docker execution modes for terminal-based agent evaluation.
|
| 448 |
+
|
| 449 |
+
+++
|
| 450 |
+
```{button-link} environments/tbench2.html
|
| 451 |
+
:color: primary
|
| 452 |
+
:outline:
|
| 453 |
+
|
| 454 |
+
{octicon}`file;1em` Docs
|
| 455 |
+
```
|
| 456 |
+
````
|
| 457 |
+
|
| 458 |
+
````{grid-item-card} Unity
|
| 459 |
+
:class-card: sd-border-1
|
| 460 |
+
|
| 461 |
+
OpenEnv wrapper for Unity ML-Agents environments, providing access to Unity's RL environments through HTTP/WebSocket interfaces.
|
| 462 |
+
|
| 463 |
+
+++
|
| 464 |
+
```{button-link} environments/unity.html
|
| 465 |
+
:color: primary
|
| 466 |
+
:outline:
|
| 467 |
+
|
| 468 |
+
{octicon}`file;1em` Docs
|
| 469 |
+
```
|
| 470 |
+
````
|
| 471 |
+
|
| 472 |
+
````{grid-item-card} Wildfire
|
| 473 |
+
:class-card: sd-border-1
|
| 474 |
+
|
| 475 |
+
Autonomous wildfire-control simulation where agents contain spreading fires using water, firebreaks, and timing under dynamic conditions.
|
| 476 |
+
|
| 477 |
+
+++
|
| 478 |
+
```{button-link} environments/wildfire.html
|
| 479 |
+
:color: primary
|
| 480 |
+
:outline:
|
| 481 |
+
|
| 482 |
+
{octicon}`file;1em` Docs
|
| 483 |
+
```
|
| 484 |
+
````
|
| 485 |
+
|
| 486 |
+
`````
|
| 487 |
+
|
| 488 |
+
```{tip}
|
| 489 |
+
Want to publish your own environment? Head over to the [Build Your Own Environment](auto_getting_started/environment-builder.md) guide for a step-by-step walkthrough.
|
| 490 |
+
```
|
| 491 |
+
|
| 492 |
+
## Community Environments
|
| 493 |
+
|
| 494 |
+
`````{grid} 1 2 3 3
|
| 495 |
+
:gutter: 3
|
| 496 |
+
|
| 497 |
+
````{grid-item-card} RLVE Gym
|
| 498 |
+
:class-card: sd-border-1
|
| 499 |
+
|
| 500 |
+
A suite of 400 environments that procedurally generate reasoning problems for LM training with configurable difficulty.
|
| 501 |
+
|
| 502 |
+
+++
|
| 503 |
+
```{button-link} https://huggingface.co/spaces/ZhiyuanZeng/RLVE_Gym/blob/main/README.md
|
| 504 |
+
:color: primary
|
| 505 |
+
:outline:
|
| 506 |
+
|
| 507 |
+
{octicon}`file;1em` Docs
|
| 508 |
+
```
|
| 509 |
+
```{button-link} https://huggingface.co/spaces/ZhiyuanZeng/RLVE_Gym
|
| 510 |
+
:color: warning
|
| 511 |
+
:outline:
|
| 512 |
+
|
| 513 |
+
๐ค Hugging Face
|
| 514 |
+
```
|
| 515 |
+
````
|
| 516 |
+
|
| 517 |
+
`````
|
| 518 |
+
|
| 519 |
+
```{toctree}
|
| 520 |
+
:hidden:
|
| 521 |
+
:maxdepth: 1
|
| 522 |
+
|
| 523 |
+
environments/echo
|
| 524 |
+
environments/coding
|
| 525 |
+
environments/chat
|
| 526 |
+
environments/atari
|
| 527 |
+
environments/openspiel
|
| 528 |
+
environments/sumo
|
| 529 |
+
environments/finrl
|
| 530 |
+
environments/textarena
|
| 531 |
+
environments/git
|
| 532 |
+
environments/dipg
|
| 533 |
+
environments/snake
|
| 534 |
+
environments/websearch
|
| 535 |
+
environments/browsergym
|
| 536 |
+
environments/repl
|
| 537 |
+
environments/calendar
|
| 538 |
+
environments/carla
|
| 539 |
+
environments/chess
|
| 540 |
+
environments/connect4
|
| 541 |
+
environments/dm_control
|
| 542 |
+
environments/finqa
|
| 543 |
+
environments/grid_world
|
| 544 |
+
environments/julia
|
| 545 |
+
environments/kernrl
|
| 546 |
+
environments/maze
|
| 547 |
+
environments/openapp
|
| 548 |
+
environments/reasoning_gym
|
| 549 |
+
environments/tbench2
|
| 550 |
+
environments/unity
|
| 551 |
+
environments/wildfire
|
| 552 |
+
```
|
OpenEnv/docs/source/environments/atari.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/atari_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/browsergym.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/browsergym_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/calendar.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/calendar_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/carla.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/carla_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/chat.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/chat_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/chess.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/chess_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/coding.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/coding_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/connect4.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/connect4_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/dipg.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/dipg_safety_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/dm_control.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/dm_control_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/echo.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/echo_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/finqa.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/finqa_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/finrl.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/finrl_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/git.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/git_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/grid_world.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/grid_world_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/julia.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/julia_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/kernrl.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/kernrl/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/maze.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/maze_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/openapp.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/openapp_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/openspiel.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/openspiel_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/reasoning_gym.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/reasoning_gym_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/repl.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/repl_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/snake.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/snake_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/sumo.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/sumo_rl_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/tbench2.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/tbench2_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/textarena.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/textarena_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/unity.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/unity_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/websearch.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/websearch_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/environments/wildfire.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```{include} ../../../envs/wildfire_env/README.md
|
| 2 |
+
```
|
OpenEnv/docs/source/getting_started/README.rst
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Quick Start
|
| 2 |
+
===========
|
| 3 |
+
|
| 4 |
+
This section provides a hands-on introduction to reinforcement learning (RL) and OpenEnv through a series of interactive tutorials. Whether you're new to RL or looking to learn how OpenEnv simplifies building and deploying environments, these tutorials will guide you through the fundamentals.
|
| 5 |
+
|
| 6 |
+
**What is OpenEnv?**
|
| 7 |
+
|
| 8 |
+
OpenEnv is a collaborative effort between **Meta, Hugging Face, Unsloth, GPU Mode, Reflection**, and other industry leaders to standardize reinforcement learning environments. Our goal is to make environment creation as easy and standardized as model sharing on Hugging Face.
|
| 9 |
+
|
| 10 |
+
Learning Path
|
| 11 |
+
-------------
|
| 12 |
+
|
| 13 |
+
The tutorials are designed to be followed in sequence, building upon concepts from previous lessons:
|
| 14 |
+
|
| 15 |
+
1. **Introduction & Quick Start** - Understand what OpenEnv is, why it exists, and run your first environment. Includes a comparison with traditional solutions like OpenAI Gym.
|
| 16 |
+
|
| 17 |
+
2. **Using Environments** - Learn how to connect to environments (Hub, Docker, URL), create AI policies, and run evaluations. Work with different games and multi-player scenarios.
|
| 18 |
+
|
| 19 |
+
3. **Building & Sharing Environments** - Create your own custom environment from scratch, package it with Docker, and share it on Hugging Face Hub.
|
| 20 |
+
|
| 21 |
+
4. **Packaging & Deploying** - The complete reference guide for creating, packaging, and deploying custom environments with the ``openenv`` CLI.
|
| 22 |
+
|
| 23 |
+
5. **Contributing to Hugging Face** - Publish, fork, and contribute to environments hosted as Hugging Face Spaces.
|
| 24 |
+
|
| 25 |
+
**No GPU Required!** All five tutorials run without a GPU.
|
| 26 |
+
|
| 27 |
+
For GPU-intensive training workflows, see the :doc:`RL Training Tutorial </tutorials/rl-training-2048>` in the Tutorials section.
|
| 28 |
+
|
| 29 |
+
Prerequisites
|
| 30 |
+
-------------
|
| 31 |
+
|
| 32 |
+
Before starting, ensure you have:
|
| 33 |
+
|
| 34 |
+
- Basic Python programming knowledge
|
| 35 |
+
- Python 3.11+ installed
|
| 36 |
+
- Docker (optional, for container-based deployment)
|
| 37 |
+
|
| 38 |
+
Running the Tutorials
|
| 39 |
+
---------------------
|
| 40 |
+
|
| 41 |
+
You can run these tutorials locally:
|
| 42 |
+
|
| 43 |
+
.. code-block:: bash
|
| 44 |
+
|
| 45 |
+
# Install OpenEnv
|
| 46 |
+
pip install openenv-core
|
| 47 |
+
|
| 48 |
+
# Run the Python scripts
|
| 49 |
+
python plot_01_introduction_quickstart.py
|
| 50 |
+
|
| 51 |
+
Or view them directly in the documentation with full code output below.
|
| 52 |
+
|
| 53 |
+
.. toctree::
|
| 54 |
+
:maxdepth: 1
|
| 55 |
+
:caption: Quick Start
|
| 56 |
+
|
| 57 |
+
plot_01_introduction_quickstart
|
| 58 |
+
plot_02_using_environments
|
| 59 |
+
plot_03_building_environments
|
| 60 |
+
environment-builder
|
| 61 |
+
contributing-envs
|
OpenEnv/docs/source/getting_started/contributing-envs.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Contributing to Hugging Face
|
| 2 |
+
|
| 3 |
+
**Part 5 of 5** in the OpenEnv Getting Started Series
|
| 4 |
+
|
| 5 |
+
OpenEnv environments are designed to be shared. The `openenv` CLI provides first-class
|
| 6 |
+
commands for publishing, forking, and contributing to environments hosted as
|
| 7 |
+
[Hugging Face Spaces](https://huggingface.co/spaces).
|
| 8 |
+
|
| 9 |
+
Envs are deployed as Hugging Face Spaces which are; Git repositories, Docker images, Python packages, and Gradio apps
|
| 10 |
+
|
| 11 |
+
This guide covers three workflows:
|
| 12 |
+
|
| 13 |
+
1. **Push** a new environment you built to the Hub.
|
| 14 |
+
2. **Fork** someone else's environment to your Hugging Face account to make changes.
|
| 15 |
+
3. **Download** an environment, make changes, and open a Pull Request.
|
| 16 |
+
|
| 17 |
+
## Prerequisites
|
| 18 |
+
|
| 19 |
+
Before you start, make sure you have:
|
| 20 |
+
|
| 21 |
+
- Python 3.11+ and [`uv`](https://github.com/astral-sh/uv) installed
|
| 22 |
+
- The OpenEnv CLI: `pip install openenv-core[cli]` (or install from source)
|
| 23 |
+
- A [Hugging Face account](https://huggingface.co/join) with a [write token](https://huggingface.co/settings/tokens)
|
| 24 |
+
|
| 25 |
+
Authenticate with the Hub:
|
| 26 |
+
|
| 27 |
+
```bash
|
| 28 |
+
hf auth login
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
The `openenv` CLI will also prompt you to log in automatically if you haven't already.
|
| 32 |
+
|
| 33 |
+
## 1. Push a New Environment to the Hub
|
| 34 |
+
|
| 35 |
+
Once you've [built an environment](environment-builder.md), publishing it to a Hugging Face Space is a single command.
|
| 36 |
+
|
| 37 |
+
```bash
|
| 38 |
+
# Push the env at '.' to the hub with config in env.yaml
|
| 39 |
+
openenv push
|
| 40 |
+
|
| 41 |
+
# Push the env to a specific repo
|
| 42 |
+
openenv push --repo-id my-org/my-custom-env
|
| 43 |
+
|
| 44 |
+
# Push the env as private
|
| 45 |
+
openenv push --private
|
| 46 |
+
|
| 47 |
+
# Push the env at 'path/to/my_env' to the hub with config in openenv.yaml
|
| 48 |
+
openenv push path/to/my_env
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
That's it. The CLI validates your environment, stages the files, adds the Hugging Face Space frontmatter, enables the web interface, and uploads everything. Your environment will be live at
|
| 52 |
+
`https://huggingface.co/spaces/<your-username>/my_env`.
|
| 53 |
+
|
| 54 |
+
```{warning}
|
| 55 |
+
If you are getting errors on deployment, it is likely because the environment structure is not valid. Run `openenv validate --verbose` to see the errors. This checks for the required files (`openenv.yaml`, `pyproject.toml`, `server/app.py`) and validates the Dockerfile and entry points.
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
## 2. Fork Someone Else's Environment
|
| 59 |
+
|
| 60 |
+
Forking creates a copy of a Hugging Face Space under your own account. This is
|
| 61 |
+
the fastest way to start experimenting with an existing environment.
|
| 62 |
+
|
| 63 |
+
```bash
|
| 64 |
+
# Fork the openenv/wordle-env environment to your account
|
| 65 |
+
openenv fork owner/space-name
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
This duplicates the Space to `<your-username>/space-name` using the same name and config. To make changes, you can fork to a specific repo name, set environment variables and secrets, and request hardware.
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
# Fork to a specific repo name
|
| 72 |
+
openenv fork openenv/wordle-env --repo-id my-username/my-wordle
|
| 73 |
+
|
| 74 |
+
# Fork the openenv/coding-env environment to your account with environment variables and secrets
|
| 75 |
+
openenv fork openenv/coding-env \
|
| 76 |
+
--set-env MODEL_ID=meta-llama/Llama-3-8B \
|
| 77 |
+
--set-secret HF_TOKEN=hf_xxxxxxxxxxxxx
|
| 78 |
+
|
| 79 |
+
# Fork the openenv/coding-env environment to your account with a GPU
|
| 80 |
+
openenv fork openenv/coding-env --hardware t4-medium
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
Once forked, you have a fully independent copy. You can:
|
| 85 |
+
|
| 86 |
+
- Visit it at `https://huggingface.co/spaces/<your-username>/<space-name>`
|
| 87 |
+
- Clone it locally to make changes (see the next section)
|
| 88 |
+
- Push updates with `openenv push`
|
| 89 |
+
|
| 90 |
+
## 3. Pull, Modify, and Open a Pull Request
|
| 91 |
+
|
| 92 |
+
The contribution workflow lets you improve an existing environment and submit
|
| 93 |
+
your changes for review, just like a GitHub Pull Request but on the Hugging
|
| 94 |
+
Face Hub.
|
| 95 |
+
|
| 96 |
+
### 3.1 Download the Space locally
|
| 97 |
+
|
| 98 |
+
Hugging Face Spaces are Git repositories. Download the one you want to contribute to:
|
| 99 |
+
|
| 100 |
+
```bash
|
| 101 |
+
hf download owner/space-name --local-dir space-name --repo-type space
|
| 102 |
+
cd space-name
|
| 103 |
+
```
|
| 104 |
+
|
| 105 |
+
```{warning}
|
| 106 |
+
If the Space is private and you have access, make sure you're logged in with
|
| 107 |
+
`hf auth login` first.
|
| 108 |
+
```
|
| 109 |
+
|
| 110 |
+
#### 3.2 Make your changes
|
| 111 |
+
Edit the environment files as needed.
|
| 112 |
+
|
| 113 |
+
```{tip}
|
| 114 |
+
You can test your changes locally before submitting:
|
| 115 |
+
|
| 116 |
+
# Run the server locally
|
| 117 |
+
cd space-name
|
| 118 |
+
uvicorn server.app:app --host 0.0.0.0 --port 8000
|
| 119 |
+
|
| 120 |
+
# Or build and run in Docker
|
| 121 |
+
openenv build
|
| 122 |
+
openenv validate --verbose
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
#### 3.3 Push your changes as a Pull Request
|
| 126 |
+
|
| 127 |
+
From the cloned directory, use `openenv push` with the `--create-pr` flag:
|
| 128 |
+
|
| 129 |
+
```bash
|
| 130 |
+
openenv push --repo-id owner/space-name --create-pr
|
| 131 |
+
```
|
| 132 |
+
|
| 133 |
+
This uploads your modified files and opens a Pull Request on the Hub. The environment owner can review your changes, leave comments, and merge them.
|
| 134 |
+
|
| 135 |
+
```{warning}
|
| 136 |
+
When using `--create-pr`, the CLI uploads your changes to a new branch and
|
| 137 |
+
opens a PR on the **original** Space. You do not need to create the Space
|
| 138 |
+
yourself.
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
### Alternative: Fork-then-PR workflow
|
| 142 |
+
|
| 143 |
+
If you prefer to develop against your own fork first, you can combine the fork
|
| 144 |
+
and PR workflows:
|
| 145 |
+
|
| 146 |
+
```bash
|
| 147 |
+
# 1. Fork the environment to your account
|
| 148 |
+
openenv fork owner/space-name --repo-id my-username/space-name
|
| 149 |
+
|
| 150 |
+
# 2. Download the forked environment to your local directory
|
| 151 |
+
hf download my-username/space-name --local-dir space-name --repo-type space
|
| 152 |
+
cd space-name
|
| 153 |
+
|
| 154 |
+
# 3. Make and test your changes
|
| 155 |
+
# ... edit files, run locally, validate ...
|
| 156 |
+
|
| 157 |
+
# 4. Push the changes back to your fork
|
| 158 |
+
openenv push
|
| 159 |
+
|
| 160 |
+
# 5. Submit a PR to the original Space
|
| 161 |
+
openenv push --repo-id owner/space-name --create-pr
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
## End-to-End Example
|
| 165 |
+
|
| 166 |
+
Here's a complete example: forking the Echo environment, adding a feature, and
|
| 167 |
+
submitting a PR.
|
| 168 |
+
|
| 169 |
+
```bash
|
| 170 |
+
# Fork the echo environment
|
| 171 |
+
openenv fork openenv/echo-env --repo-id my-username/echo-env-improved
|
| 172 |
+
|
| 173 |
+
# Clone your fork
|
| 174 |
+
git clone https://huggingface.co/spaces/my-username/echo-env-improved
|
| 175 |
+
cd echo-env-improved
|
| 176 |
+
|
| 177 |
+
# Make changes (e.g., add a timestamp to observations)
|
| 178 |
+
# ... edit server/echo_environment.py ...
|
| 179 |
+
|
| 180 |
+
# Test locally
|
| 181 |
+
openenv validate --verbose
|
| 182 |
+
|
| 183 |
+
# Push your improvement as a PR to the original
|
| 184 |
+
openenv push --repo-id openenv/echo-env --create-pr
|
| 185 |
+
```
|
| 186 |
+
|
| 187 |
+
## Next Steps
|
| 188 |
+
|
| 189 |
+
- [Build your own environment from scratch](environment-builder.md)
|
| 190 |
+
- [Customize the web UI](../customizing-web-ui.md)
|
| 191 |
+
- [Browse available environments](../environments.md)
|
| 192 |
+
- [End-to-end tutorial](../tutorials/openenv-tutorial.md)
|
OpenEnv/docs/source/getting_started/environment-builder.md
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Packaging & Deploying
|
| 2 |
+
|
| 3 |
+
**Part 4 of 5** in the OpenEnv Getting Started Series
|
| 4 |
+
|
| 5 |
+
This guide walks you through creating a custom environment using the `OpenEnv` framework and the `openenv` CLI.
|
| 6 |
+
|
| 7 |
+
The CLI handles scaffolding, builds, validation, and deployment so you can stay focused on environment logic.
|
| 8 |
+
|
| 9 |
+
```{note}
|
| 10 |
+
**New to OpenEnv?** If you're just getting started, we recommend completing the [Getting Started tutorials](index) first. They provide a conceptual introduction to OpenEnv and reinforcement learning fundamentals. This guide is for developers ready to build production-quality environments.
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
## Quick Reference Card
|
| 14 |
+
|
| 15 |
+
Already familiar with OpenEnv? Here's the 8-step process at a glance:
|
| 16 |
+
|
| 17 |
+
| Step | Command / Action | Description |
|
| 18 |
+
|------|------------------|-------------|
|
| 19 |
+
| 1 | `openenv init my_env` | Scaffold new environment |
|
| 20 |
+
| 2 | Edit `models.py` | Define Action & Observation dataclasses |
|
| 21 |
+
| 3 | Edit `server/my_environment.py` | Implement `reset()` and `step()` methods |
|
| 22 |
+
| 4 | Edit `client.py` | Implement `_step_payload()`, `_parse_result()`, `_parse_state()` |
|
| 23 |
+
| 5 | `openenv serve` | Start local dev server for testing |
|
| 24 |
+
| 6 | `openenv validate` | Validate environment structure |
|
| 25 |
+
| 7 | `openenv push` | Deploy to Hugging Face Hub |
|
| 26 |
+
| 8 | Share the URL! | Others use via `MyEnv.from_hub("you/my-env")` |
|
| 27 |
+
|
| 28 |
+
### CLI Quick Reference
|
| 29 |
+
|
| 30 |
+
| Command | Description |
|
| 31 |
+
|---------|-------------|
|
| 32 |
+
| `openenv init NAME` | Scaffold new environment |
|
| 33 |
+
| `openenv serve` | Start local dev server |
|
| 34 |
+
| `openenv build` | Build Docker image |
|
| 35 |
+
| `openenv validate --verbose` | Validate environment structure |
|
| 36 |
+
| `openenv push` | Deploy to Hugging Face Hub |
|
| 37 |
+
| `openenv push --repo-id NAME` | Deploy to specific repo |
|
| 38 |
+
| `openenv push --private` | Deploy as private environment |
|
| 39 |
+
| `openenv push --registry ghcr.io/ORG` | Push to GitHub Container Registry |
|
| 40 |
+
|
| 41 |
+
```{tip}
|
| 42 |
+
For a hands-on tutorial that builds a complete environment step-by-step, see [Building & Sharing Environments](plot_03_building_environments) in the Getting Started series.
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## Overview
|
| 48 |
+
|
| 49 |
+
A typical workflow looks like:
|
| 50 |
+
|
| 51 |
+
1. Scaffold a new environment with `openenv init`.
|
| 52 |
+
2. Customize your models, environment logic, and FastAPI server.
|
| 53 |
+
3. Implement a typed `EnvClient` (WebSocket-based for persistent sessions).
|
| 54 |
+
4. Configure dependencies and the Dockerfile once.
|
| 55 |
+
5. Use the CLI (`openenv build`, `openenv validate`, `openenv push`) to package and share your work.
|
| 56 |
+
|
| 57 |
+
```{note}
|
| 58 |
+
These integrations are handled automatically by the `openenv` CLI when you run `openenv init`.
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
### Prerequisites
|
| 62 |
+
|
| 63 |
+
- Python 3.11+ and [`uv`](https://github.com/astral-sh/uv) for dependency locking
|
| 64 |
+
- Docker Desktop / Docker Engine
|
| 65 |
+
- The OpenEnv library installed: `pip install https://github.com/meta-pytorch/OpenEnv.git`
|
| 66 |
+
|
| 67 |
+
## Step-by-Step Guide
|
| 68 |
+
|
| 69 |
+
Let's walk through the process of building a custom environment with OpenEnv.
|
| 70 |
+
|
| 71 |
+
### 1. Scaffold with `openenv init`
|
| 72 |
+
|
| 73 |
+
```bash
|
| 74 |
+
# Run from anywhere โ defaults to current directory
|
| 75 |
+
openenv init my_env
|
| 76 |
+
|
| 77 |
+
# Optionally choose an output directory
|
| 78 |
+
openenv init my_env --output-dir /Users/you/envs
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
The command creates a fully-typed template with `openenv.yaml`, `pyproject.toml`, `uv.lock`, Docker assets, and stub implementations. If you're working inside this repo, move the generated folder under `envs/`.
|
| 82 |
+
|
| 83 |
+
Typical layout:
|
| 84 |
+
|
| 85 |
+
```
|
| 86 |
+
my_env/
|
| 87 |
+
โโโ __init__.py
|
| 88 |
+
โโโ README.md
|
| 89 |
+
โโโ client.py
|
| 90 |
+
โโโ models.py
|
| 91 |
+
โโโ openenv.yaml
|
| 92 |
+
โโโ pyproject.toml
|
| 93 |
+
โโโ uv.lock
|
| 94 |
+
โโโ server/
|
| 95 |
+
โโโ __init__.py
|
| 96 |
+
โโโ app.py
|
| 97 |
+
โโโ my_environment.py
|
| 98 |
+
โโโ requirements.txt
|
| 99 |
+
โโโ Dockerfile
|
| 100 |
+
```
|
| 101 |
+
|
| 102 |
+
Python classes are generated for the action, observation, environment, and client. For example, you will find `MyEnvironment`, `MyAction`, `MyObservation`, and `MyEnv` (client) in the `my_env` directory based on the name you provided. The environment uses the core `State` class from `openenv.core.env_server.types`.
|
| 103 |
+
|
| 104 |
+
### 2. Define Models
|
| 105 |
+
|
| 106 |
+
Edit `models.py` to describe your action and observation using Pydantic:
|
| 107 |
+
|
| 108 |
+
```python
|
| 109 |
+
# models.py
|
| 110 |
+
from pydantic import Field
|
| 111 |
+
from openenv.core.env_server.types import Action, Observation
|
| 112 |
+
|
| 113 |
+
class MyAction(Action):
|
| 114 |
+
"""Your custom action."""
|
| 115 |
+
command: str = Field(..., description="Command to execute")
|
| 116 |
+
parameters: dict = Field(default_factory=dict, description="Command parameters")
|
| 117 |
+
|
| 118 |
+
class MyObservation(Observation):
|
| 119 |
+
"""Your custom observation."""
|
| 120 |
+
result: str = Field(..., description="Result of the action")
|
| 121 |
+
success: bool = Field(..., description="Whether the action succeeded")
|
| 122 |
+
```
|
| 123 |
+
|
| 124 |
+
### 3. Implement Environment Logic
|
| 125 |
+
|
| 126 |
+
Customize `server/my_environment.py` by extending `Environment`:
|
| 127 |
+
|
| 128 |
+
```python
|
| 129 |
+
# server/my_environment.py
|
| 130 |
+
from uuid import uuid4
|
| 131 |
+
from openenv.core.env_server.interfaces import Environment
|
| 132 |
+
from openenv.core.env_server.types import State
|
| 133 |
+
from models import MyAction, MyObservation
|
| 134 |
+
|
| 135 |
+
class MyEnvironment(Environment):
|
| 136 |
+
def __init__(self):
|
| 137 |
+
self._state = State(episode_id=str(uuid4()), step_count=0)
|
| 138 |
+
|
| 139 |
+
def reset(self) -> MyObservation:
|
| 140 |
+
self._state = State(episode_id=str(uuid4()), step_count=0)
|
| 141 |
+
return MyObservation(result="Ready", success=True, done=False, reward=0.0)
|
| 142 |
+
|
| 143 |
+
def step(self, action: MyAction) -> MyObservation:
|
| 144 |
+
# Implement your logic here
|
| 145 |
+
self._state.step_count += 1
|
| 146 |
+
result = self._execute_command(action.command)
|
| 147 |
+
return MyObservation(result=result, success=True, done=False, reward=1.0)
|
| 148 |
+
|
| 149 |
+
@property
|
| 150 |
+
def state(self) -> State:
|
| 151 |
+
return self._state
|
| 152 |
+
```
|
| 153 |
+
|
| 154 |
+
### 4. Create the FastAPI Server
|
| 155 |
+
|
| 156 |
+
`server/app.py` should expose the environment through `create_app`.
|
| 157 |
+
|
| 158 |
+
**Important:** You must pass a class or factory function (not an instance) to enable WebSocket-based concurrent sessions:
|
| 159 |
+
|
| 160 |
+
```python
|
| 161 |
+
# server/app.py
|
| 162 |
+
from openenv.core.env_server import create_app
|
| 163 |
+
from ..models import MyAction, MyObservation
|
| 164 |
+
from .my_environment import MyEnvironment
|
| 165 |
+
|
| 166 |
+
# Pass the class (factory) - each WebSocket session gets its own instance
|
| 167 |
+
app = create_app(MyEnvironment, MyAction, MyObservation, env_name="my_env")
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
For environments with constructor arguments, create a factory function:
|
| 171 |
+
|
| 172 |
+
```python
|
| 173 |
+
# server/app.py
|
| 174 |
+
import os
|
| 175 |
+
from openenv.core.env_server import create_app
|
| 176 |
+
from ..models import MyAction, MyObservation
|
| 177 |
+
from .my_environment import MyEnvironment
|
| 178 |
+
|
| 179 |
+
# Read config from environment variables
|
| 180 |
+
api_key = os.getenv("MY_API_KEY")
|
| 181 |
+
timeout = int(os.getenv("MY_TIMEOUT", "30"))
|
| 182 |
+
|
| 183 |
+
def create_my_environment():
|
| 184 |
+
"""Factory function that creates MyEnvironment with config."""
|
| 185 |
+
return MyEnvironment(api_key=api_key, timeout=timeout)
|
| 186 |
+
|
| 187 |
+
# Pass the factory function
|
| 188 |
+
app = create_app(create_my_environment, MyAction, MyObservation, env_name="my_env")
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
### 5. Implement the Client
|
| 192 |
+
|
| 193 |
+
`client.py` extends `EnvClient` so users can interact with your server via WebSocket for persistent sessions:
|
| 194 |
+
|
| 195 |
+
```python
|
| 196 |
+
# client.py
|
| 197 |
+
from openenv.core.env_client import EnvClient
|
| 198 |
+
from openenv.core.client_types import StepResult
|
| 199 |
+
from .models import MyAction, MyObservation, MyState
|
| 200 |
+
|
| 201 |
+
class MyEnv(EnvClient[MyAction, MyObservation, MyState]):
|
| 202 |
+
def _step_payload(self, action: MyAction) -> dict:
|
| 203 |
+
return {"command": action.command, "parameters": action.parameters}
|
| 204 |
+
|
| 205 |
+
def _parse_result(self, payload: dict) -> StepResult[MyObservation]:
|
| 206 |
+
obs_data = payload.get("observation", {})
|
| 207 |
+
obs = MyObservation(
|
| 208 |
+
result=obs_data.get("result", ""),
|
| 209 |
+
success=obs_data.get("success", False),
|
| 210 |
+
done=payload.get("done", False),
|
| 211 |
+
reward=payload.get("reward"),
|
| 212 |
+
)
|
| 213 |
+
return StepResult(
|
| 214 |
+
observation=obs,
|
| 215 |
+
reward=payload.get("reward"),
|
| 216 |
+
done=payload.get("done", False),
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
def _parse_state(self, payload: dict) -> State:
|
| 220 |
+
return State(
|
| 221 |
+
episode_id=payload.get("episode_id"),
|
| 222 |
+
step_count=payload.get("step_count", 0),
|
| 223 |
+
)
|
| 224 |
+
```
|
| 225 |
+
|
| 226 |
+
The `EnvClient` maintains a persistent WebSocket connection to the server, enabling efficient multi-step interactions with lower latency compared to HTTP. Each client instance gets its own dedicated environment session on the server.
|
| 227 |
+
|
| 228 |
+
### 6. Configure Dependencies & Dockerfile
|
| 229 |
+
|
| 230 |
+
The CLI template ships with `pyproject.toml` and `server/Dockerfile`. You should manage your python dependencies with `uv` or `pip` in the `pyproject.toml` file. Other dependencies should be installed in the Dockerfile.
|
| 231 |
+
|
| 232 |
+
Keep building from the `openenv-base` image so shared tooling stays available:
|
| 233 |
+
|
| 234 |
+
<details>
|
| 235 |
+
<summary>Dockerfile</summary>
|
| 236 |
+
|
| 237 |
+
```dockerfile
|
| 238 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 239 |
+
# All rights reserved.
|
| 240 |
+
#
|
| 241 |
+
# This source code is licensed under the BSD-style license found in the
|
| 242 |
+
# LICENSE file in the root directory of this source tree.
|
| 243 |
+
|
| 244 |
+
# Multi-stage build using openenv-base
|
| 245 |
+
# This Dockerfile is flexible and works for both:
|
| 246 |
+
# - In-repo environments (with local src/core)
|
| 247 |
+
# - Standalone environments (with openenv from pip)
|
| 248 |
+
# The build script (openenv build) handles context detection and sets appropriate build args.
|
| 249 |
+
|
| 250 |
+
ARG BASE_IMAGE=openenv-base:latest
|
| 251 |
+
FROM ${BASE_IMAGE} AS builder
|
| 252 |
+
|
| 253 |
+
WORKDIR /app
|
| 254 |
+
|
| 255 |
+
# Build argument to control whether we're building standalone or in-repo
|
| 256 |
+
ARG BUILD_MODE=in-repo
|
| 257 |
+
ARG ENV_NAME=__ENV_NAME__
|
| 258 |
+
|
| 259 |
+
# Copy environment code (always at root of build context)
|
| 260 |
+
COPY . /app/env
|
| 261 |
+
|
| 262 |
+
# For in-repo builds, openenv is already in the pyproject.toml dependencies
|
| 263 |
+
# For standalone builds, openenv will be installed from pip via pyproject.toml
|
| 264 |
+
WORKDIR /app/env
|
| 265 |
+
|
| 266 |
+
# Install dependencies using uv sync
|
| 267 |
+
# If uv.lock exists, use it; otherwise resolve on the fly
|
| 268 |
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 269 |
+
if [ -f uv.lock ]; then \
|
| 270 |
+
uv sync --frozen --no-install-project --no-editable; \
|
| 271 |
+
else \
|
| 272 |
+
uv sync --no-install-project --no-editable; \
|
| 273 |
+
fi
|
| 274 |
+
|
| 275 |
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 276 |
+
if [ -f uv.lock ]; then \
|
| 277 |
+
uv sync --frozen --no-editable; \
|
| 278 |
+
else \
|
| 279 |
+
uv sync --no-editable; \
|
| 280 |
+
fi
|
| 281 |
+
|
| 282 |
+
# Final runtime stage
|
| 283 |
+
FROM ${BASE_IMAGE}
|
| 284 |
+
|
| 285 |
+
WORKDIR /app
|
| 286 |
+
|
| 287 |
+
# Copy the virtual environment from builder
|
| 288 |
+
COPY --from=builder /app/env/.venv /app/.venv
|
| 289 |
+
|
| 290 |
+
# Copy the environment code
|
| 291 |
+
COPY --from=builder /app/env /app/env
|
| 292 |
+
|
| 293 |
+
# Set PATH to use the virtual environment
|
| 294 |
+
ENV PATH="/app/.venv/bin:$PATH"
|
| 295 |
+
|
| 296 |
+
# Set PYTHONPATH so imports work correctly
|
| 297 |
+
ENV PYTHONPATH="/app/env:$PYTHONPATH"
|
| 298 |
+
|
| 299 |
+
# Health check
|
| 300 |
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
| 301 |
+
CMD curl -f http://localhost:8000/health || exit 1
|
| 302 |
+
|
| 303 |
+
# Run the FastAPI server
|
| 304 |
+
# The module path is constructed to work with the /app/env structure
|
| 305 |
+
CMD ["sh", "-c", "cd /app/env && uvicorn server.app:app --host 0.0.0.0 --port 8000"]
|
| 306 |
+
|
| 307 |
+
```
|
| 308 |
+
|
| 309 |
+
</details>
|
| 310 |
+
|
| 311 |
+
If you introduced extra dependencies in the Dockerfile, you should install them in the Dockerfile before removing temp files.
|
| 312 |
+
|
| 313 |
+
### 7. Build & Validate with the CLI
|
| 314 |
+
|
| 315 |
+
From the environment directory:
|
| 316 |
+
|
| 317 |
+
```bash
|
| 318 |
+
cd envs/my_env
|
| 319 |
+
openenv build # Builds Docker image (auto-detects context)
|
| 320 |
+
openenv validate --verbose
|
| 321 |
+
```
|
| 322 |
+
|
| 323 |
+
`openenv build` understands both standalone environments and in-repo ones. Useful flags:
|
| 324 |
+
|
| 325 |
+
- `--tag/-t`: override the default `openenv-<env_name>` tag
|
| 326 |
+
- `--build-arg KEY=VALUE`: pass multiple Docker build arguments
|
| 327 |
+
- `--dockerfile` / `--context`: custom locations when experimenting
|
| 328 |
+
- `--no-cache`: force fresh dependency installs
|
| 329 |
+
|
| 330 |
+
`openenv validate` checks for required files, ensures the Dockerfile/server entrypoints function, and lists supported deployment modes. The command exits non-zero if issues are found so you can wire it into CI.
|
| 331 |
+
|
| 332 |
+
### 8. Push & Share with `openenv push`
|
| 333 |
+
|
| 334 |
+
Once validation passes, the CLI can deploy directly to Hugging Face Spaces or any registry:
|
| 335 |
+
|
| 336 |
+
```bash
|
| 337 |
+
# Push to HF Spaces (auto enables web UI and prompts for login if needed)
|
| 338 |
+
openenv push
|
| 339 |
+
|
| 340 |
+
# Push to a specific repo or namespace
|
| 341 |
+
openenv push --repo-id my-org/my-env
|
| 342 |
+
|
| 343 |
+
# Push to Docker/ghcr (interface disabled by default)
|
| 344 |
+
openenv push --registry ghcr.io/my-org --tag my-env:latest
|
| 345 |
+
|
| 346 |
+
# Customize image base or visibility
|
| 347 |
+
openenv push --base-image ghcr.io/meta-pytorch/openenv-base:latest --private
|
| 348 |
+
```
|
| 349 |
+
|
| 350 |
+
Key options:
|
| 351 |
+
|
| 352 |
+
- `--directory`: path to the environment (defaults to `cwd`)
|
| 353 |
+
- `--repo-id`: explicit Hugging Face space name
|
| 354 |
+
- `--registry`: push to Docker Hub, GHCR, etc.
|
| 355 |
+
- `--interface/--no-interface`: toggle the optional web UI
|
| 356 |
+
- `--base-image`: override the Dockerfile `FROM`
|
| 357 |
+
- `--private`: mark the space as private
|
| 358 |
+
|
| 359 |
+
The command validates your `openenv.yaml`, injects Hugging Face frontmatter when needed, and uploads the prepared bundle.
|
| 360 |
+
|
| 361 |
+
### 9. Automate Builds (optional)
|
| 362 |
+
|
| 363 |
+
To trigger Docker builds on every push to `main`, add your environment to the matrix in `.github/workflows/docker-build.yml`:
|
| 364 |
+
|
| 365 |
+
```yaml
|
| 366 |
+
strategy:
|
| 367 |
+
matrix:
|
| 368 |
+
image:
|
| 369 |
+
- name: echo-env
|
| 370 |
+
dockerfile: envs/echo_env/server/Dockerfile
|
| 371 |
+
- name: chat-env
|
| 372 |
+
dockerfile: envs/chat_env/server/Dockerfile
|
| 373 |
+
- name: coding-env
|
| 374 |
+
dockerfile: envs/coding_env/server/Dockerfile
|
| 375 |
+
- name: my-env # Add your environment here
|
| 376 |
+
dockerfile: envs/my_env/server/Dockerfile
|
| 377 |
+
```
|
| 378 |
+
|
| 379 |
+
### Use Your Environment
|
| 380 |
+
|
| 381 |
+
Here is a simple example of using your environment:
|
| 382 |
+
|
| 383 |
+
```python
|
| 384 |
+
from envs.my_env import MyAction, MyEnv
|
| 385 |
+
|
| 386 |
+
# Create environment from Docker image
|
| 387 |
+
client = MyEnv.from_docker_image("my-env:latest")
|
| 388 |
+
# Or, connect to the remote space on Hugging Face
|
| 389 |
+
client = MyEnv.from_hub("my-org/my-env")
|
| 390 |
+
# Or, connect to the local server
|
| 391 |
+
client = MyEnv(base_url="http://localhost:8000")
|
| 392 |
+
|
| 393 |
+
# Use context manager for automatic cleanup (recommended)
|
| 394 |
+
with client:
|
| 395 |
+
# Reset
|
| 396 |
+
result = client.reset()
|
| 397 |
+
print(result.observation.result) # "Ready"
|
| 398 |
+
|
| 399 |
+
# Execute actions
|
| 400 |
+
result = client.step(MyAction(command="test", parameters={}))
|
| 401 |
+
print(result.observation.result)
|
| 402 |
+
print(result.observation.success)
|
| 403 |
+
|
| 404 |
+
# Get state
|
| 405 |
+
state = client.state()
|
| 406 |
+
print(state.episode_id)
|
| 407 |
+
print(state.step_count)
|
| 408 |
+
|
| 409 |
+
# Or manually manage the connection
|
| 410 |
+
try:
|
| 411 |
+
client = MyEnv(base_url="http://localhost:8000")
|
| 412 |
+
result = client.reset()
|
| 413 |
+
result = client.step(MyAction(command="test", parameters={}))
|
| 414 |
+
finally:
|
| 415 |
+
client.close()
|
| 416 |
+
```
|
| 417 |
+
|
| 418 |
+
## Nice work! You've now built and used your own OpenEnv environment.
|
| 419 |
+
|
| 420 |
+
Your next steps are to:
|
| 421 |
+
|
| 422 |
+
- [Try out the end-to-end tutorial](https://colab.research.google.com/github/meta-pytorch/OpenEnv/blob/main/examples/OpenEnv_Tutorial.ipynb)
|
OpenEnv/docs/source/getting_started/plot_01_introduction_quickstart.py
ADDED
|
@@ -0,0 +1,774 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Introduction & Quick Start
|
| 3 |
+
==========================
|
| 4 |
+
|
| 5 |
+
**Part 1 of 5** in the OpenEnv Getting Started Series
|
| 6 |
+
|
| 7 |
+
This notebook introduces OpenEnv, explains why it exists, and gets you
|
| 8 |
+
running your first environment.
|
| 9 |
+
|
| 10 |
+
.. note::
|
| 11 |
+
**Time**: ~10 minutes | **Difficulty**: Beginner | **GPU Required**: No
|
| 12 |
+
|
| 13 |
+
What You'll Learn
|
| 14 |
+
-----------------
|
| 15 |
+
|
| 16 |
+
- **What is OpenEnv**: The unified framework for RL environments
|
| 17 |
+
- **Why OpenEnv**: How it compares to traditional solutions like Gym
|
| 18 |
+
- **RL Basics**: The observe-act-reward loop in 60 seconds
|
| 19 |
+
- **Quick Start**: Connect to and interact with your first environment
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
# %%
|
| 23 |
+
# Setup: Enable nested async event loops
|
| 24 |
+
# --------------------------------------
|
| 25 |
+
#
|
| 26 |
+
# This is needed when running in environments like Sphinx-Gallery or Jupyter
|
| 27 |
+
# that already have an event loop running.
|
| 28 |
+
|
| 29 |
+
import nest_asyncio
|
| 30 |
+
nest_asyncio.apply()
|
| 31 |
+
|
| 32 |
+
# %%
|
| 33 |
+
# What is OpenEnv?
|
| 34 |
+
# ----------------
|
| 35 |
+
#
|
| 36 |
+
# OpenEnv is a **unified framework for building, sharing, and interacting with
|
| 37 |
+
# reinforcement learning environments**. It's a collaborative effort between
|
| 38 |
+
# Meta, Hugging Face, Unsloth, GPU Mode, and other industry leaders.
|
| 39 |
+
#
|
| 40 |
+
# **The Goal**: Make environment creation as easy and standardized as model
|
| 41 |
+
# sharing on Hugging Face.
|
| 42 |
+
#
|
| 43 |
+
# Key Features
|
| 44 |
+
# ~~~~~~~~~~~~
|
| 45 |
+
#
|
| 46 |
+
# - **Standardized API**: Gymnasium-style ``reset()``, ``step()``, ``state()``
|
| 47 |
+
# - **Type-Safe**: Full IDE autocomplete and error checking
|
| 48 |
+
# - **Containerized**: Environments run in Docker for isolation and reproducibility
|
| 49 |
+
# - **Shareable**: Push to Hugging Face Hub with one command
|
| 50 |
+
# - **Language-Agnostic**: HTTP/WebSocket API works from any language
|
| 51 |
+
|
| 52 |
+
# %%
|
| 53 |
+
# RL in 60 Seconds
|
| 54 |
+
# ----------------
|
| 55 |
+
#
|
| 56 |
+
# Reinforcement Learning is simpler than you think. It's just a loop:
|
| 57 |
+
#
|
| 58 |
+
# .. code-block:: text
|
| 59 |
+
#
|
| 60 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 61 |
+
# โ THE RL LOOP โ
|
| 62 |
+
# โ โ
|
| 63 |
+
# โ โโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
|
| 64 |
+
# โ โ AGENT โโactionโโถโ ENVIRONMENT โ โ
|
| 65 |
+
# โ โ โโโrewardโโ โ โ
|
| 66 |
+
# โ โ โโโโobsโโโโ โ โ
|
| 67 |
+
# โ โโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
|
| 68 |
+
# โ โ
|
| 69 |
+
# โ 1. Agent observes the environment โ
|
| 70 |
+
# โ 2. Agent chooses an action โ
|
| 71 |
+
# โ 3. Environment returns reward + new observation โ
|
| 72 |
+
# โ 4. Repeat until done โ
|
| 73 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 74 |
+
#
|
| 75 |
+
# In code, it looks like this:
|
| 76 |
+
#
|
| 77 |
+
# .. code-block:: python
|
| 78 |
+
#
|
| 79 |
+
# result = env.reset() # Start episode
|
| 80 |
+
# while not result.done:
|
| 81 |
+
# action = agent.choose(result.observation)
|
| 82 |
+
# result = env.step(action) # Take action, get reward
|
| 83 |
+
# agent.learn(result.reward)
|
| 84 |
+
#
|
| 85 |
+
# That's it. That's RL!
|
| 86 |
+
|
| 87 |
+
# %%
|
| 88 |
+
# Why OpenEnv? (vs. Traditional Solutions)
|
| 89 |
+
# ----------------------------------------
|
| 90 |
+
#
|
| 91 |
+
# Traditional RL environments (like OpenAI Gym/Gymnasium) have been the backbone
|
| 92 |
+
# of RL research for years. They provide a simple API for interacting with
|
| 93 |
+
# environments, and the community has built thousands of environments on top of them.
|
| 94 |
+
#
|
| 95 |
+
# However, as RL moves from research to production, several challenges emerge:
|
| 96 |
+
#
|
| 97 |
+
# The Problem with Traditional Approaches
|
| 98 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 99 |
+
#
|
| 100 |
+
# 1. **No Type Safety**: Observations are numpy arrays like ``obs[0][3]``. What does
|
| 101 |
+
# index 3 mean? You have to read documentation or source code to find out.
|
| 102 |
+
#
|
| 103 |
+
# 2. **Same-Process Execution**: The environment runs in your training process.
|
| 104 |
+
# A bug in the environment can crash your entire training run.
|
| 105 |
+
#
|
| 106 |
+
# 3. **Dependency Hell**: Sharing environments means copying files and hoping
|
| 107 |
+
# the recipient has the same dependencies installed.
|
| 108 |
+
#
|
| 109 |
+
# 4. **Python Lock-in**: Want to use Rust or C++ for your agent? Too badโGym is Python-only.
|
| 110 |
+
#
|
| 111 |
+
# 5. **"Works on My Machine"**: Environments behave differently on different systems
|
| 112 |
+
# due to floating-point differences, library versions, or OS quirks.
|
| 113 |
+
#
|
| 114 |
+
# How OpenEnv Solves These Problems
|
| 115 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 116 |
+
#
|
| 117 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 118 |
+
# | Challenge | Traditional (Gym) | OpenEnv |
|
| 119 |
+
# +==================+==================================+==================================+
|
| 120 |
+
# | **Type Safety** | ``obs[0][3]`` - what is it? | ``obs.info_state`` - IDE knows! |
|
| 121 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 122 |
+
# | **Isolation** | Same process (can crash) | Docker container (isolated) |
|
| 123 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 124 |
+
# | **Deployment** | "Works on my machine" | Same container everywhere |
|
| 125 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 126 |
+
# | **Sharing** | Copy files, manage deps | ``openenv push`` to Hub |
|
| 127 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 128 |
+
# | **Language** | Python only | Any language (HTTP/WebSocket) |
|
| 129 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 130 |
+
# | **Scaling** | Single machine | Deploy to Kubernetes |
|
| 131 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 132 |
+
# | **Debugging** | Cryptic numpy index errors | Clear, typed error messages |
|
| 133 |
+
# +------------------+----------------------------------+----------------------------------+
|
| 134 |
+
#
|
| 135 |
+
# Side-by-Side Code Comparison
|
| 136 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 137 |
+
#
|
| 138 |
+
# Let's compare the same workflow in both approaches:
|
| 139 |
+
#
|
| 140 |
+
# **Traditional Gym approach:**
|
| 141 |
+
#
|
| 142 |
+
# .. code-block:: python
|
| 143 |
+
#
|
| 144 |
+
# import gym
|
| 145 |
+
# import numpy as np
|
| 146 |
+
#
|
| 147 |
+
# # Create environment - runs in your process
|
| 148 |
+
# env = gym.make("CartPole-v1")
|
| 149 |
+
#
|
| 150 |
+
# # Reset returns numpy arrays
|
| 151 |
+
# obs, info = env.reset()
|
| 152 |
+
# # obs = array([0.01, 0.02, -0.03, 0.01])
|
| 153 |
+
# # What do these numbers mean? You have to check docs!
|
| 154 |
+
#
|
| 155 |
+
# # Step returns multiple values
|
| 156 |
+
# obs, reward, done, truncated, info = env.step(action)
|
| 157 |
+
# # No IDE autocomplete, easy to mix up return values
|
| 158 |
+
#
|
| 159 |
+
# # If env crashes, your whole training crashes
|
| 160 |
+
# # Sharing requires: pip install gym[atari], hope versions match
|
| 161 |
+
#
|
| 162 |
+
# **OpenEnv approach:**
|
| 163 |
+
#
|
| 164 |
+
# .. code-block:: python
|
| 165 |
+
#
|
| 166 |
+
# from openenv import AutoEnv, AutoAction
|
| 167 |
+
#
|
| 168 |
+
# # Load environment and action classes via auto-discovery
|
| 169 |
+
# OpenSpielEnv = AutoEnv.get_env_class("openspiel")
|
| 170 |
+
# OpenSpielAction = AutoAction.from_env("openspiel")
|
| 171 |
+
#
|
| 172 |
+
# # Connect to containerized environment
|
| 173 |
+
# with OpenSpielEnv(base_url="http://localhost:8000") as env:
|
| 174 |
+
# # Reset returns typed StepResult
|
| 175 |
+
# result = env.reset()
|
| 176 |
+
# # result.observation.legal_actions - IDE autocompletes!
|
| 177 |
+
# # result.observation.info_state - you know exactly what this is
|
| 178 |
+
#
|
| 179 |
+
# # Step with typed action
|
| 180 |
+
# action = OpenSpielAction(action_id=1, game_name="catch")
|
| 181 |
+
# result = env.step(action)
|
| 182 |
+
# # result.reward, result.done - all typed
|
| 183 |
+
#
|
| 184 |
+
# # Environment runs in Docker - isolated from your code
|
| 185 |
+
# # Share via: openenv push my-env (one command!)
|
| 186 |
+
|
| 187 |
+
# %%
|
| 188 |
+
# Part 1: Environment Setup
|
| 189 |
+
# -------------------------
|
| 190 |
+
#
|
| 191 |
+
# Let's set up our environment. This works in Google Colab, locally, or
|
| 192 |
+
# anywhere Python runs.
|
| 193 |
+
|
| 194 |
+
import subprocess
|
| 195 |
+
import sys
|
| 196 |
+
from pathlib import Path
|
| 197 |
+
|
| 198 |
+
# Detect environment
|
| 199 |
+
try:
|
| 200 |
+
import google.colab
|
| 201 |
+
|
| 202 |
+
IN_COLAB = True
|
| 203 |
+
except ImportError:
|
| 204 |
+
IN_COLAB = False
|
| 205 |
+
|
| 206 |
+
if IN_COLAB:
|
| 207 |
+
print("=" * 70)
|
| 208 |
+
print(" GOOGLE COLAB DETECTED - Installing OpenEnv...")
|
| 209 |
+
print("=" * 70)
|
| 210 |
+
|
| 211 |
+
# Install OpenEnv
|
| 212 |
+
subprocess.run(
|
| 213 |
+
[sys.executable, "-m", "pip", "install", "-q", "openenv-core"],
|
| 214 |
+
capture_output=True,
|
| 215 |
+
)
|
| 216 |
+
print(" OpenEnv installed!")
|
| 217 |
+
print("=" * 70)
|
| 218 |
+
else:
|
| 219 |
+
print("=" * 70)
|
| 220 |
+
print(" RUNNING LOCALLY")
|
| 221 |
+
print("=" * 70)
|
| 222 |
+
print()
|
| 223 |
+
print("If you haven't installed OpenEnv yet:")
|
| 224 |
+
print(" pip install openenv-core")
|
| 225 |
+
print()
|
| 226 |
+
|
| 227 |
+
# Add src to path for local development (when running from docs folder)
|
| 228 |
+
src_path = Path.cwd().parent.parent.parent / "src"
|
| 229 |
+
if src_path.exists():
|
| 230 |
+
sys.path.insert(0, str(src_path))
|
| 231 |
+
|
| 232 |
+
# Add envs to path
|
| 233 |
+
envs_path = Path.cwd().parent.parent.parent / "envs"
|
| 234 |
+
if envs_path.exists():
|
| 235 |
+
sys.path.insert(0, str(envs_path.parent))
|
| 236 |
+
|
| 237 |
+
print("=" * 70)
|
| 238 |
+
|
| 239 |
+
print()
|
| 240 |
+
print("Ready to explore OpenEnv!")
|
| 241 |
+
|
| 242 |
+
# %%
|
| 243 |
+
# Part 2: Your First Environment - OpenSpiel
|
| 244 |
+
# -------------------------------------------
|
| 245 |
+
#
|
| 246 |
+
# What is OpenSpiel?
|
| 247 |
+
# ~~~~~~~~~~~~~~~~~~
|
| 248 |
+
#
|
| 249 |
+
# `OpenSpiel <https://github.com/google-deepmind/open_spiel>`_ is an open-source
|
| 250 |
+
# collection of **70+ game environments** developed by DeepMind for research in
|
| 251 |
+
# reinforcement learning, game theory, and multi-agent systems.
|
| 252 |
+
#
|
| 253 |
+
# It includes:
|
| 254 |
+
#
|
| 255 |
+
# - **Classic board games**: Chess, Go, Backgammon, Tic-Tac-Toe
|
| 256 |
+
# - **Card games**: Poker variants, Blackjack, Bridge
|
| 257 |
+
# - **Simple RL benchmarks**: Catch, Cliff Walking, 2048
|
| 258 |
+
# - **Multi-agent games**: Hanabi, Kuhn Poker, Negotiation games
|
| 259 |
+
#
|
| 260 |
+
# OpenSpiel is widely used in RL research because it provides consistent,
|
| 261 |
+
# well-tested implementations with support for both single-player and multi-player
|
| 262 |
+
# scenarios.
|
| 263 |
+
#
|
| 264 |
+
# How OpenSpiel Connects to OpenEnv
|
| 265 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 266 |
+
#
|
| 267 |
+
# OpenEnv wraps OpenSpiel games as **containerized, type-safe environments**.
|
| 268 |
+
# This means:
|
| 269 |
+
#
|
| 270 |
+
# 1. You get all the benefits of OpenSpiel's game library
|
| 271 |
+
# 2. Plus type-safe Python clients with IDE autocomplete
|
| 272 |
+
# 3. Plus Docker isolation for reproducibility
|
| 273 |
+
# 4. Plus easy sharing via Hugging Face Hub
|
| 274 |
+
#
|
| 275 |
+
# Currently, OpenEnv includes wrappers for 6 OpenSpiel games:
|
| 276 |
+
#
|
| 277 |
+
# +------------------+-------------+------------------------------------------+
|
| 278 |
+
# | Game | Players | Description |
|
| 279 |
+
# +==================+=============+==========================================+
|
| 280 |
+
# | **Catch** | 1 | Catch a falling ball with a paddle |
|
| 281 |
+
# +------------------+-------------+------------------------------------------+
|
| 282 |
+
# | **2048** | 1 | Slide tiles to combine numbers |
|
| 283 |
+
# +------------------+-------------+------------------------------------------+
|
| 284 |
+
# | **Blackjack** | 1 | Classic card game against dealer |
|
| 285 |
+
# +------------------+-------------+------------------------------------------+
|
| 286 |
+
# | **Cliff Walking**| 1 | Navigate a grid while avoiding cliffs |
|
| 287 |
+
# +------------------+-------------+------------------------------------------+
|
| 288 |
+
# | **Tic-Tac-Toe** | 2 | Classic 3ร3 grid game |
|
| 289 |
+
# +------------------+-------------+------------------------------------------+
|
| 290 |
+
# | **Kuhn Poker** | 2 | Simplified 3-card poker |
|
| 291 |
+
# +------------------+-------------+------------------------------------------+
|
| 292 |
+
#
|
| 293 |
+
# The Catch Game
|
| 294 |
+
# ~~~~~~~~~~~~~~
|
| 295 |
+
#
|
| 296 |
+
# For this tutorial, we'll use **Catch**โone of the simplest RL environments.
|
| 297 |
+
# It's perfect for learning because:
|
| 298 |
+
#
|
| 299 |
+
# - Simple rules (easy to understand)
|
| 300 |
+
# - Fast episodes (10 steps each)
|
| 301 |
+
# - Clear success metric (did you catch the ball?)
|
| 302 |
+
# - Optimal strategy is learnable (move toward the ball)
|
| 303 |
+
#
|
| 304 |
+
# **Game Rules:**
|
| 305 |
+
#
|
| 306 |
+
# .. code-block:: text
|
| 307 |
+
#
|
| 308 |
+
# โฌ โฌ ๐ด โฌ โฌ <- Ball starts at random column (row 0)
|
| 309 |
+
# โฌ โฌ โฌ โฌ โฌ
|
| 310 |
+
# โฌ โฌ โฌ โฌ โฌ The ball falls down one row
|
| 311 |
+
# โฌ โฌ โฌ โฌ โฌ each time step
|
| 312 |
+
# โฌ โฌ โฌ โฌ โฌ
|
| 313 |
+
# โฌ โฌ โฌ โฌ โฌ
|
| 314 |
+
# โฌ โฌ โฌ โฌ โฌ
|
| 315 |
+
# โฌ โฌ โฌ โฌ โฌ
|
| 316 |
+
# โฌ โฌ โฌ โฌ โฌ
|
| 317 |
+
# โฌ โฌ ๐ โฌ โฌ <- Paddle at bottom (row 9)
|
| 318 |
+
#
|
| 319 |
+
# - **Grid Size**: 10 rows ร 5 columns
|
| 320 |
+
# - **Ball**: Starts at a random column in row 0, falls one row per step
|
| 321 |
+
# - **Paddle**: Starts at center column, you control it
|
| 322 |
+
# - **Episode Length**: 10 steps (ball reaches bottom)
|
| 323 |
+
#
|
| 324 |
+
# **Actions:**
|
| 325 |
+
#
|
| 326 |
+
# +------------+------------------+
|
| 327 |
+
# | Action ID | Movement |
|
| 328 |
+
# +============+==================+
|
| 329 |
+
# | 0 | Move LEFT |
|
| 330 |
+
# +------------+------------------+
|
| 331 |
+
# | 1 | STAY (no move) |
|
| 332 |
+
# +------------+------------------+
|
| 333 |
+
# | 2 | Move RIGHT |
|
| 334 |
+
# +------------+------------------+
|
| 335 |
+
#
|
| 336 |
+
# **Rewards:**
|
| 337 |
+
#
|
| 338 |
+
# - **+1.0** if the paddle is in the same column as the ball when it lands
|
| 339 |
+
# - **0.0** if you miss the ball
|
| 340 |
+
#
|
| 341 |
+
# **Optimal Strategy**: Track the ball's column and move toward it. A perfect
|
| 342 |
+
# policy wins 100% of the time since the paddle can always reach any column
|
| 343 |
+
# in 10 steps (grid is only 5 columns wide).
|
| 344 |
+
#
|
| 345 |
+
# Importing OpenEnv
|
| 346 |
+
# ~~~~~~~~~~~~~~~~~
|
| 347 |
+
#
|
| 348 |
+
# First, let's import the OpenSpiel environment client and models:
|
| 349 |
+
|
| 350 |
+
# Real imports from OpenEnv
|
| 351 |
+
try:
|
| 352 |
+
# Direct imports from the openspiel_env package
|
| 353 |
+
from openspiel_env.client import OpenSpielEnv
|
| 354 |
+
from openspiel_env.models import OpenSpielAction, OpenSpielObservation, OpenSpielState
|
| 355 |
+
|
| 356 |
+
OPENENV_AVAILABLE = True
|
| 357 |
+
print("โ OpenEnv imports successful!")
|
| 358 |
+
print(f" - OpenSpielEnv: {OpenSpielEnv}")
|
| 359 |
+
print(f" - OpenSpielAction: {OpenSpielAction}")
|
| 360 |
+
except ImportError as e:
|
| 361 |
+
OPENENV_AVAILABLE = False
|
| 362 |
+
print(f"โ OpenEnv not fully installed: {e}")
|
| 363 |
+
print(" Run: pip install openenv-core")
|
| 364 |
+
print(" And: pip install -e ./envs/openspiel_env")
|
| 365 |
+
|
| 366 |
+
# %%
|
| 367 |
+
# Connecting to an Environment
|
| 368 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 369 |
+
#
|
| 370 |
+
# OpenEnv provides three ways to connect to environments:
|
| 371 |
+
#
|
| 372 |
+
# 1. **From Hugging Face Hub** (auto-downloads and starts container)
|
| 373 |
+
# 2. **From Docker image** (uses local image)
|
| 374 |
+
# 3. **From URL** (connects to running server)
|
| 375 |
+
#
|
| 376 |
+
# Let's examine the actual methods available on the client class:
|
| 377 |
+
|
| 378 |
+
print("=" * 70)
|
| 379 |
+
print(" THREE WAYS TO CONNECT")
|
| 380 |
+
print("=" * 70)
|
| 381 |
+
print()
|
| 382 |
+
|
| 383 |
+
if OPENENV_AVAILABLE:
|
| 384 |
+
# Show actual method signatures from the class
|
| 385 |
+
import inspect
|
| 386 |
+
|
| 387 |
+
print("Connection methods available on OpenSpielEnv:")
|
| 388 |
+
print()
|
| 389 |
+
|
| 390 |
+
# Method 1: from_hub
|
| 391 |
+
if hasattr(OpenSpielEnv, "from_hub"):
|
| 392 |
+
sig = inspect.signature(OpenSpielEnv.from_hub)
|
| 393 |
+
print(f"1. OpenSpielEnv.from_hub{sig}")
|
| 394 |
+
print(" โ Auto-downloads from Hugging Face, starts container, connects")
|
| 395 |
+
print(" Example: env = OpenSpielEnv.from_hub('openenv/openspiel-env')")
|
| 396 |
+
print()
|
| 397 |
+
|
| 398 |
+
# Method 2: from_docker_image
|
| 399 |
+
if hasattr(OpenSpielEnv, "from_docker_image"):
|
| 400 |
+
sig = inspect.signature(OpenSpielEnv.from_docker_image)
|
| 401 |
+
print(f"2. OpenSpielEnv.from_docker_image{sig}")
|
| 402 |
+
print(" โ Starts container from local image, connects")
|
| 403 |
+
print(" Example: env = OpenSpielEnv.from_docker_image('openspiel-env:latest')")
|
| 404 |
+
print()
|
| 405 |
+
|
| 406 |
+
# Method 3: Direct connection
|
| 407 |
+
sig = inspect.signature(OpenSpielEnv.__init__)
|
| 408 |
+
print(f"3. OpenSpielEnv.__init__{sig}")
|
| 409 |
+
print(" โ Connects to already-running server")
|
| 410 |
+
print(" Example: env = OpenSpielEnv(base_url='http://localhost:8000')")
|
| 411 |
+
print()
|
| 412 |
+
|
| 413 |
+
print("-" * 70)
|
| 414 |
+
print("All three give you the same API - just different ways to start!")
|
| 415 |
+
else:
|
| 416 |
+
print("(OpenEnv not installed - showing expected methods)")
|
| 417 |
+
print()
|
| 418 |
+
print("1. OpenSpielEnv.from_hub(repo_id, *, use_docker=True, ...)")
|
| 419 |
+
print(" โ Auto-downloads from Hugging Face, starts container, connects")
|
| 420 |
+
print()
|
| 421 |
+
print("2. OpenSpielEnv.from_docker_image(image, provider=None, ...)")
|
| 422 |
+
print(" โ Starts container from local image, connects")
|
| 423 |
+
print()
|
| 424 |
+
print("3. OpenSpielEnv(base_url, connect_timeout_s=10.0, ...)")
|
| 425 |
+
print(" โ Connects to already-running server")
|
| 426 |
+
|
| 427 |
+
# %%
|
| 428 |
+
# Part 3: Playing the Catch Game
|
| 429 |
+
# ------------------------------
|
| 430 |
+
#
|
| 431 |
+
# Now let's actually play! This code attempts to connect to a real server.
|
| 432 |
+
# If no server is running, we'll show what the interaction looks like.
|
| 433 |
+
|
| 434 |
+
import random
|
| 435 |
+
|
| 436 |
+
# Check if we can connect to a server
|
| 437 |
+
SERVER_URL = "http://localhost:8000"
|
| 438 |
+
SERVER_AVAILABLE = False
|
| 439 |
+
|
| 440 |
+
if OPENENV_AVAILABLE:
|
| 441 |
+
try:
|
| 442 |
+
# Try to connect using sync wrapper
|
| 443 |
+
env = OpenSpielEnv(base_url=SERVER_URL)
|
| 444 |
+
with env.sync() as client:
|
| 445 |
+
# Quick test to verify connection
|
| 446 |
+
pass
|
| 447 |
+
SERVER_AVAILABLE = True
|
| 448 |
+
print(f"โ Connected to server at {SERVER_URL}")
|
| 449 |
+
except Exception as e:
|
| 450 |
+
print(f"โ No server running at {SERVER_URL}")
|
| 451 |
+
print(f" Error: {e}")
|
| 452 |
+
print()
|
| 453 |
+
print("To start a server, run one of these:")
|
| 454 |
+
print(" docker run -p 8000:8000 openenv/openspiel-env:latest")
|
| 455 |
+
print(" # OR")
|
| 456 |
+
print(" cd envs/openspiel_env && openenv serve")
|
| 457 |
+
|
| 458 |
+
# %%
|
| 459 |
+
# Playing with a Real Server
|
| 460 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 461 |
+
#
|
| 462 |
+
# When connected to a real server, here's how the interaction works:
|
| 463 |
+
|
| 464 |
+
if OPENENV_AVAILABLE and SERVER_AVAILABLE:
|
| 465 |
+
print("=" * 70)
|
| 466 |
+
print(" PLAYING CATCH - LIVE!")
|
| 467 |
+
print("=" * 70)
|
| 468 |
+
|
| 469 |
+
env = OpenSpielEnv(base_url=SERVER_URL)
|
| 470 |
+
with env.sync() as client:
|
| 471 |
+
# Reset to start a new episode
|
| 472 |
+
result = client.reset()
|
| 473 |
+
|
| 474 |
+
print(f"\nEpisode started!")
|
| 475 |
+
print(f" Observation type: {type(result.observation).__name__}")
|
| 476 |
+
print(f" Legal actions: {result.observation.legal_actions}")
|
| 477 |
+
print(f" Done: {result.done}")
|
| 478 |
+
|
| 479 |
+
# Play until the episode ends
|
| 480 |
+
step_count = 0
|
| 481 |
+
while not result.done:
|
| 482 |
+
# Choose a random action from legal actions
|
| 483 |
+
action_id = random.choice(result.observation.legal_actions)
|
| 484 |
+
action = OpenSpielAction(action_id=action_id, game_name="catch")
|
| 485 |
+
|
| 486 |
+
# Take the action
|
| 487 |
+
result = client.step(action)
|
| 488 |
+
step_count += 1
|
| 489 |
+
|
| 490 |
+
print(f"\nStep {step_count}:")
|
| 491 |
+
print(f" Action: {action_id} ({'LEFT' if action_id == 0 else 'STAY' if action_id == 1 else 'RIGHT'})")
|
| 492 |
+
print(f" Reward: {result.reward}")
|
| 493 |
+
print(f" Done: {result.done}")
|
| 494 |
+
|
| 495 |
+
# Get final state
|
| 496 |
+
state = client.state()
|
| 497 |
+
print(f"\nEpisode complete!")
|
| 498 |
+
print(f" Total steps: {state.step_count}")
|
| 499 |
+
print(f" Final reward: {result.reward}")
|
| 500 |
+
print(f" Result: {'CAUGHT!' if result.reward > 0 else 'MISSED!'}")
|
| 501 |
+
|
| 502 |
+
else:
|
| 503 |
+
# Run a local simulation to demonstrate the gameplay
|
| 504 |
+
print("=" * 70)
|
| 505 |
+
print(" PLAYING CATCH - LOCAL SIMULATION")
|
| 506 |
+
print("=" * 70)
|
| 507 |
+
print()
|
| 508 |
+
print("No server running - demonstrating with local simulation.")
|
| 509 |
+
print("(This shows exactly what happens when playing the real game)")
|
| 510 |
+
print()
|
| 511 |
+
|
| 512 |
+
# Simulate the Catch game locally
|
| 513 |
+
GRID_HEIGHT = 10
|
| 514 |
+
GRID_WIDTH = 5
|
| 515 |
+
|
| 516 |
+
# Initialize game state
|
| 517 |
+
ball_col = random.randint(0, GRID_WIDTH - 1)
|
| 518 |
+
paddle_col = GRID_WIDTH // 2 # Start in center
|
| 519 |
+
|
| 520 |
+
print(f"Game initialized:")
|
| 521 |
+
print(f" Ball starting column: {ball_col}")
|
| 522 |
+
print(f" Paddle starting column: {paddle_col}")
|
| 523 |
+
print(f" Grid size: {GRID_HEIGHT} rows ร {GRID_WIDTH} columns")
|
| 524 |
+
print()
|
| 525 |
+
|
| 526 |
+
# Simulate episode
|
| 527 |
+
for step in range(GRID_HEIGHT):
|
| 528 |
+
# Create observation (matching OpenSpiel format)
|
| 529 |
+
info_state = [0.0] * (GRID_HEIGHT * GRID_WIDTH)
|
| 530 |
+
info_state[step * GRID_WIDTH + ball_col] = 1.0 # Ball position
|
| 531 |
+
info_state[(GRID_HEIGHT - 1) * GRID_WIDTH + paddle_col] = 1.0 # Paddle
|
| 532 |
+
|
| 533 |
+
legal_actions = [0, 1, 2] # LEFT, STAY, RIGHT
|
| 534 |
+
|
| 535 |
+
# Choose random action
|
| 536 |
+
action_id = random.choice(legal_actions)
|
| 537 |
+
action_name = {0: "LEFT", 1: "STAY", 2: "RIGHT"}[action_id]
|
| 538 |
+
|
| 539 |
+
# Execute action
|
| 540 |
+
old_paddle = paddle_col
|
| 541 |
+
if action_id == 0: # LEFT
|
| 542 |
+
paddle_col = max(0, paddle_col - 1)
|
| 543 |
+
elif action_id == 2: # RIGHT
|
| 544 |
+
paddle_col = min(GRID_WIDTH - 1, paddle_col + 1)
|
| 545 |
+
|
| 546 |
+
print(f"Step {step + 1}: Ball at row {step}, col {ball_col} | "
|
| 547 |
+
f"Paddle: {old_paddle}โ{paddle_col} ({action_name})")
|
| 548 |
+
|
| 549 |
+
# Determine result
|
| 550 |
+
caught = (paddle_col == ball_col)
|
| 551 |
+
reward = 1.0 if caught else 0.0
|
| 552 |
+
|
| 553 |
+
print()
|
| 554 |
+
print(f"Episode complete!")
|
| 555 |
+
print(f" Ball landed at column: {ball_col}")
|
| 556 |
+
print(f" Paddle final column: {paddle_col}")
|
| 557 |
+
print(f" Reward: {reward}")
|
| 558 |
+
print(f" Result: {'CAUGHT! ๐' if caught else 'MISSED! ๐ข'}")
|
| 559 |
+
print()
|
| 560 |
+
print("-" * 70)
|
| 561 |
+
print("This is exactly how the real OpenSpielEnv works,")
|
| 562 |
+
print("just running locally instead of via WebSocket to a server.")
|
| 563 |
+
|
| 564 |
+
# %%
|
| 565 |
+
# Part 4: Understanding the Response Types
|
| 566 |
+
# ----------------------------------------
|
| 567 |
+
#
|
| 568 |
+
# OpenEnv uses type-safe models for all interactions. Let's create actual
|
| 569 |
+
# instances and examine their attributes:
|
| 570 |
+
|
| 571 |
+
print("=" * 70)
|
| 572 |
+
print(" OPENENV TYPE SYSTEM - ACTUAL INSTANCES")
|
| 573 |
+
print("=" * 70)
|
| 574 |
+
|
| 575 |
+
# Create example instances that match what you'd get from the Catch game
|
| 576 |
+
# These are the actual Pydantic models used by OpenEnv
|
| 577 |
+
|
| 578 |
+
# 1. OpenSpielObservation - what the agent receives after each step
|
| 579 |
+
print("\n๐ฆ OpenSpielObservation (returned in StepResult)")
|
| 580 |
+
print("-" * 50)
|
| 581 |
+
|
| 582 |
+
if OPENENV_AVAILABLE:
|
| 583 |
+
# OpenSpielObservation was already imported above via auto-discovery
|
| 584 |
+
# Create a sample observation like what Catch game returns
|
| 585 |
+
sample_observation = OpenSpielObservation(
|
| 586 |
+
info_state=[0.0, 0.0, 1.0, 0.0, 0.0] + [0.0] * 45, # Ball at col 2, row 0
|
| 587 |
+
legal_actions=[0, 1, 2], # LEFT, STAY, RIGHT
|
| 588 |
+
game_phase="playing",
|
| 589 |
+
current_player_id=0,
|
| 590 |
+
opponent_last_action=None,
|
| 591 |
+
)
|
| 592 |
+
|
| 593 |
+
print(f" info_state: {sample_observation.info_state[:10]}... (length: {len(sample_observation.info_state)})")
|
| 594 |
+
print(f" legal_actions: {sample_observation.legal_actions}")
|
| 595 |
+
print(f" game_phase: {sample_observation.game_phase!r}")
|
| 596 |
+
print(f" current_player_id: {sample_observation.current_player_id}")
|
| 597 |
+
print(f" opponent_last_action: {sample_observation.opponent_last_action}")
|
| 598 |
+
else:
|
| 599 |
+
# Create without imports to show the structure
|
| 600 |
+
from dataclasses import dataclass
|
| 601 |
+
from typing import List, Optional
|
| 602 |
+
|
| 603 |
+
@dataclass
|
| 604 |
+
class OpenSpielObservation:
|
| 605 |
+
info_state: List[float]
|
| 606 |
+
legal_actions: List[int]
|
| 607 |
+
game_phase: str = "playing"
|
| 608 |
+
current_player_id: int = 0
|
| 609 |
+
opponent_last_action: Optional[int] = None
|
| 610 |
+
|
| 611 |
+
sample_observation = OpenSpielObservation(
|
| 612 |
+
info_state=[0.0, 0.0, 1.0, 0.0, 0.0] + [0.0] * 45,
|
| 613 |
+
legal_actions=[0, 1, 2],
|
| 614 |
+
game_phase="playing",
|
| 615 |
+
current_player_id=0,
|
| 616 |
+
opponent_last_action=None,
|
| 617 |
+
)
|
| 618 |
+
|
| 619 |
+
print(f" info_state: {sample_observation.info_state[:10]}... (length: {len(sample_observation.info_state)})")
|
| 620 |
+
print(f" legal_actions: {sample_observation.legal_actions}")
|
| 621 |
+
print(f" game_phase: {sample_observation.game_phase!r}")
|
| 622 |
+
print(f" current_player_id: {sample_observation.current_player_id}")
|
| 623 |
+
print(f" opponent_last_action: {sample_observation.opponent_last_action}")
|
| 624 |
+
|
| 625 |
+
# 2. OpenSpielState - the environment's internal state
|
| 626 |
+
print("\n๐ OpenSpielState (returned by state())")
|
| 627 |
+
print("-" * 50)
|
| 628 |
+
|
| 629 |
+
if OPENENV_AVAILABLE:
|
| 630 |
+
# OpenSpielState was already imported above via auto-discovery
|
| 631 |
+
sample_state = OpenSpielState(
|
| 632 |
+
game_name="catch",
|
| 633 |
+
agent_player=0,
|
| 634 |
+
opponent_policy="random",
|
| 635 |
+
game_params={"rows": 10, "columns": 5},
|
| 636 |
+
num_players=1,
|
| 637 |
+
)
|
| 638 |
+
|
| 639 |
+
print(f" game_name: {sample_state.game_name!r}")
|
| 640 |
+
print(f" agent_player: {sample_state.agent_player}")
|
| 641 |
+
print(f" opponent_policy: {sample_state.opponent_policy!r}")
|
| 642 |
+
print(f" game_params: {sample_state.game_params}")
|
| 643 |
+
print(f" num_players: {sample_state.num_players}")
|
| 644 |
+
else:
|
| 645 |
+
@dataclass
|
| 646 |
+
class OpenSpielState:
|
| 647 |
+
game_name: str = "catch"
|
| 648 |
+
agent_player: int = 0
|
| 649 |
+
opponent_policy: str = "random"
|
| 650 |
+
game_params: dict = None
|
| 651 |
+
num_players: int = 1
|
| 652 |
+
|
| 653 |
+
sample_state = OpenSpielState(
|
| 654 |
+
game_name="catch",
|
| 655 |
+
agent_player=0,
|
| 656 |
+
opponent_policy="random",
|
| 657 |
+
game_params={"rows": 10, "columns": 5},
|
| 658 |
+
num_players=1,
|
| 659 |
+
)
|
| 660 |
+
|
| 661 |
+
print(f" game_name: {sample_state.game_name!r}")
|
| 662 |
+
print(f" agent_player: {sample_state.agent_player}")
|
| 663 |
+
print(f" opponent_policy: {sample_state.opponent_policy!r}")
|
| 664 |
+
print(f" game_params: {sample_state.game_params}")
|
| 665 |
+
print(f" num_players: {sample_state.num_players}")
|
| 666 |
+
|
| 667 |
+
# 3. OpenSpielAction - what you send to step()
|
| 668 |
+
print("\n๐ฎ OpenSpielAction (what you send to step())")
|
| 669 |
+
print("-" * 50)
|
| 670 |
+
|
| 671 |
+
if OPENENV_AVAILABLE:
|
| 672 |
+
# OpenSpielAction was already imported above via auto-discovery
|
| 673 |
+
sample_action = OpenSpielAction(
|
| 674 |
+
action_id=1, # STAY
|
| 675 |
+
game_name="catch",
|
| 676 |
+
game_params={"rows": 10, "columns": 5},
|
| 677 |
+
)
|
| 678 |
+
|
| 679 |
+
print(f" action_id: {sample_action.action_id} # 0=LEFT, 1=STAY, 2=RIGHT")
|
| 680 |
+
print(f" game_name: {sample_action.game_name!r}")
|
| 681 |
+
print(f" game_params: {sample_action.game_params}")
|
| 682 |
+
else:
|
| 683 |
+
@dataclass
|
| 684 |
+
class OpenSpielAction:
|
| 685 |
+
action_id: int
|
| 686 |
+
game_name: str = "catch"
|
| 687 |
+
game_params: dict = None
|
| 688 |
+
|
| 689 |
+
sample_action = OpenSpielAction(
|
| 690 |
+
action_id=1,
|
| 691 |
+
game_name="catch",
|
| 692 |
+
game_params={"rows": 10, "columns": 5},
|
| 693 |
+
)
|
| 694 |
+
|
| 695 |
+
print(f" action_id: {sample_action.action_id} # 0=LEFT, 1=STAY, 2=RIGHT")
|
| 696 |
+
print(f" game_name: {sample_action.game_name!r}")
|
| 697 |
+
print(f" game_params: {sample_action.game_params}")
|
| 698 |
+
|
| 699 |
+
print("\n" + "=" * 70)
|
| 700 |
+
print("These are the actual Pydantic/dataclass models used by OpenEnv.")
|
| 701 |
+
print("Type safety helps catch errors before they reach the environment!")
|
| 702 |
+
print("=" * 70)
|
| 703 |
+
|
| 704 |
+
# %%
|
| 705 |
+
# Part 5: The Architecture
|
| 706 |
+
# ------------------------
|
| 707 |
+
#
|
| 708 |
+
# OpenEnv uses a client-server architecture:
|
| 709 |
+
#
|
| 710 |
+
# .. code-block:: text
|
| 711 |
+
#
|
| 712 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 713 |
+
# โ YOUR CODE โ
|
| 714 |
+
# โ โ
|
| 715 |
+
# โ from openenv import AutoEnv โ
|
| 716 |
+
# โ OpenSpielEnv = AutoEnv.get_env_class("openspiel") โ
|
| 717 |
+
# โ env = OpenSpielEnv(base_url="http://localhost:8000") โ
|
| 718 |
+
# โ result = env.reset() # Sends WebSocket message โ
|
| 719 |
+
# โ result = env.step(action) # Sends WebSocket message โ
|
| 720 |
+
# โ โ
|
| 721 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 722 |
+
# โ
|
| 723 |
+
# โ WebSocket (persistent connection)
|
| 724 |
+
# โ
|
| 725 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 726 |
+
# โ DOCKER CONTAINER โ
|
| 727 |
+
# โ โ
|
| 728 |
+
# โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
|
| 729 |
+
# โ โ FastAPI Server + Environment Logic โ โ
|
| 730 |
+
# โ โ - /ws (WebSocket endpoint) โ โ
|
| 731 |
+
# โ โ - Handles reset(), step(), state() โ โ
|
| 732 |
+
# โ โ - Runs the actual game simulation โ โ
|
| 733 |
+
# โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
|
| 734 |
+
# โ โ
|
| 735 |
+
# โ Isolated โข Reproducible โข Scalable โ
|
| 736 |
+
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 737 |
+
#
|
| 738 |
+
# **Key insight**: You never deal with HTTP/WebSocket directly.
|
| 739 |
+
# The OpenEnv client handles all the networking!
|
| 740 |
+
|
| 741 |
+
# %%
|
| 742 |
+
# Summary
|
| 743 |
+
# -------
|
| 744 |
+
#
|
| 745 |
+
# In this notebook, you learned:
|
| 746 |
+
#
|
| 747 |
+
# **What OpenEnv Is:**
|
| 748 |
+
#
|
| 749 |
+
# - A unified framework for RL environments
|
| 750 |
+
# - Containerized, type-safe, and shareable
|
| 751 |
+
#
|
| 752 |
+
# **Why Use OpenEnv:**
|
| 753 |
+
#
|
| 754 |
+
# - Type safety with IDE autocomplete
|
| 755 |
+
# - Isolated Docker containers
|
| 756 |
+
# - Easy sharing via Hugging Face Hub
|
| 757 |
+
#
|
| 758 |
+
# **How to Use It:**
|
| 759 |
+
#
|
| 760 |
+
# - ``env.reset()`` - Start a new episode
|
| 761 |
+
# - ``env.step(action)`` - Take an action
|
| 762 |
+
# - ``env.state()`` - Get current state
|
| 763 |
+
#
|
| 764 |
+
# Next Steps
|
| 765 |
+
# ----------
|
| 766 |
+
#
|
| 767 |
+
# **Continue to Notebook 2: Using Environments**
|
| 768 |
+
#
|
| 769 |
+
# In the next notebook, you'll:
|
| 770 |
+
#
|
| 771 |
+
# - Explore all available OpenEnv environments
|
| 772 |
+
# - Create different AI policies
|
| 773 |
+
# - Run evaluations and compare performance
|
| 774 |
+
# - Work with multi-player games
|