kaustubhg73 commited on
Commit
e5f64b3
ยท
verified ยท
1 Parent(s): 1c254e0

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes. ย  See raw diff
Files changed (50) hide show
  1. .gitattributes +13 -0
  2. Dockerfile +13 -0
  3. OpenEnv/CLAUDE.md +258 -0
  4. OpenEnv/CODE_OF_CONDUCT.md +80 -0
  5. OpenEnv/CONTRIBUTING.md +37 -0
  6. OpenEnv/LICENSE +28 -0
  7. OpenEnv/MANIFEST.in +2 -0
  8. OpenEnv/README.md +381 -0
  9. OpenEnv/docs/Makefile +25 -0
  10. OpenEnv/docs/README.md +189 -0
  11. OpenEnv/docs/source/_static/versions.json +8 -0
  12. OpenEnv/docs/source/auto_discovery.md +431 -0
  13. OpenEnv/docs/source/cli.md +86 -0
  14. OpenEnv/docs/source/conf.py +206 -0
  15. OpenEnv/docs/source/core.md +215 -0
  16. OpenEnv/docs/source/customizing-web-ui.md +89 -0
  17. OpenEnv/docs/source/environments.md +552 -0
  18. OpenEnv/docs/source/environments/atari.md +2 -0
  19. OpenEnv/docs/source/environments/browsergym.md +2 -0
  20. OpenEnv/docs/source/environments/calendar.md +2 -0
  21. OpenEnv/docs/source/environments/carla.md +2 -0
  22. OpenEnv/docs/source/environments/chat.md +2 -0
  23. OpenEnv/docs/source/environments/chess.md +2 -0
  24. OpenEnv/docs/source/environments/coding.md +2 -0
  25. OpenEnv/docs/source/environments/connect4.md +2 -0
  26. OpenEnv/docs/source/environments/dipg.md +2 -0
  27. OpenEnv/docs/source/environments/dm_control.md +2 -0
  28. OpenEnv/docs/source/environments/echo.md +2 -0
  29. OpenEnv/docs/source/environments/finqa.md +2 -0
  30. OpenEnv/docs/source/environments/finrl.md +2 -0
  31. OpenEnv/docs/source/environments/git.md +2 -0
  32. OpenEnv/docs/source/environments/grid_world.md +2 -0
  33. OpenEnv/docs/source/environments/julia.md +2 -0
  34. OpenEnv/docs/source/environments/kernrl.md +2 -0
  35. OpenEnv/docs/source/environments/maze.md +2 -0
  36. OpenEnv/docs/source/environments/openapp.md +2 -0
  37. OpenEnv/docs/source/environments/openspiel.md +2 -0
  38. OpenEnv/docs/source/environments/reasoning_gym.md +2 -0
  39. OpenEnv/docs/source/environments/repl.md +2 -0
  40. OpenEnv/docs/source/environments/snake.md +2 -0
  41. OpenEnv/docs/source/environments/sumo.md +2 -0
  42. OpenEnv/docs/source/environments/tbench2.md +2 -0
  43. OpenEnv/docs/source/environments/textarena.md +2 -0
  44. OpenEnv/docs/source/environments/unity.md +2 -0
  45. OpenEnv/docs/source/environments/websearch.md +2 -0
  46. OpenEnv/docs/source/environments/wildfire.md +2 -0
  47. OpenEnv/docs/source/getting_started/README.rst +61 -0
  48. OpenEnv/docs/source/getting_started/contributing-envs.md +192 -0
  49. OpenEnv/docs/source/getting_started/environment-builder.md +422 -0
  50. 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
+ [![PyPI](https://img.shields.io/pypi/v/openenv-core?color=blue)](https://pypi.org/project/openenv-core/)
6
+ [![Discord](https://img.shields.io/badge/Discord-OpenEnv-7289da?style=flat&logo=discord&logoColor=white)](https://discord.gg/YsTYBh6PD9)
7
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/meta-pytorch/OpenEnv/blob/main/examples/OpenEnv_Tutorial.ipynb)
8
+ [![Docs](https://img.shields.io/badge/Docs-Explore-blue?logo=readthedocs&logoColor=white)](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