openenv 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. {openenv-0.2.1 → openenv-0.2.2}/PKG-INFO +76 -32
  2. openenv-0.2.1/src/openenv.egg-info/PKG-INFO → openenv-0.2.2/README.md +53 -69
  3. {openenv-0.2.1 → openenv-0.2.2}/pyproject.toml +61 -2
  4. openenv-0.2.2/src/openenv/__init__.py +54 -0
  5. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/auto/_discovery.py +2 -2
  6. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/auto/auto_action.py +5 -5
  7. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/auto/auto_env.py +37 -26
  8. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/__main__.py +10 -2
  9. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/_cli_utils.py +17 -14
  10. openenv-0.2.2/src/openenv/cli/_validation.py +594 -0
  11. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/commands/__init__.py +2 -2
  12. openenv-0.2.2/src/openenv/cli/commands/fork.py +197 -0
  13. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/commands/init.py +0 -2
  14. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/commands/push.py +198 -21
  15. openenv-0.2.2/src/openenv/cli/commands/skills.py +200 -0
  16. openenv-0.2.2/src/openenv/cli/commands/validate.py +198 -0
  17. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/client.py +2 -2
  18. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/models.py +1 -2
  19. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/pyproject.toml +1 -1
  20. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/server/__ENV_NAME___environment.py +4 -1
  21. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/server/app.py +6 -3
  22. openenv-0.2.2/src/openenv/core/__init__.py +81 -0
  23. openenv-0.2.2/src/openenv/core/containers/runtime/daytona_provider.py +572 -0
  24. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/containers/runtime/providers.py +2 -0
  25. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/containers/test_local_docker_provider.py +0 -1
  26. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_client.py +145 -45
  27. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/__init__.py +62 -33
  28. openenv-0.2.2/src/openenv/core/env_server/gradio_theme.py +128 -0
  29. openenv-0.2.2/src/openenv/core/env_server/gradio_ui.py +240 -0
  30. openenv-0.2.2/src/openenv/core/env_server/http_server.py +1646 -0
  31. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/interfaces.py +104 -3
  32. openenv-0.2.2/src/openenv/core/env_server/mcp_environment.py +672 -0
  33. openenv-0.2.2/src/openenv/core/env_server/mcp_types.py +321 -0
  34. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/serialization.py +35 -1
  35. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/types.py +50 -6
  36. openenv-0.2.2/src/openenv/core/env_server/web_interface.py +644 -0
  37. openenv-0.2.2/src/openenv/core/evals/__init__.py +18 -0
  38. openenv-0.2.2/src/openenv/core/evals/base.py +62 -0
  39. openenv-0.2.2/src/openenv/core/evals/inspect_harness.py +160 -0
  40. openenv-0.2.2/src/openenv/core/evals/types.py +40 -0
  41. openenv-0.2.2/src/openenv/core/llm_client.py +506 -0
  42. openenv-0.2.2/src/openenv/core/mcp_client.py +484 -0
  43. openenv-0.2.2/src/openenv/core/rubrics/__init__.py +40 -0
  44. openenv-0.2.2/src/openenv/core/rubrics/base.py +195 -0
  45. openenv-0.2.2/src/openenv/core/rubrics/containers.py +574 -0
  46. openenv-0.2.2/src/openenv/core/rubrics/llm_judge.py +118 -0
  47. openenv-0.2.2/src/openenv/core/rubrics/trajectory.py +203 -0
  48. openenv-0.2.2/src/openenv/core/sync_client.py +263 -0
  49. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/tools/__init__.py +6 -1
  50. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/tools/local_python_executor.py +2 -4
  51. openenv-0.2.2/src/openenv/core/utils.py +59 -0
  52. openenv-0.2.1/README.md → openenv-0.2.2/src/openenv.egg-info/PKG-INFO +113 -30
  53. {openenv-0.2.1 → openenv-0.2.2}/src/openenv.egg-info/SOURCES.txt +16 -0
  54. {openenv-0.2.1 → openenv-0.2.2}/src/openenv.egg-info/requires.txt +25 -1
  55. {openenv-0.2.1 → openenv-0.2.2}/src/openenv_core/__init__.py +9 -1
  56. openenv-0.2.1/src/openenv/__init__.py +0 -15
  57. openenv-0.2.1/src/openenv/cli/_validation.py +0 -161
  58. openenv-0.2.1/src/openenv/cli/commands/validate.py +0 -108
  59. openenv-0.2.1/src/openenv/core/__init__.py +0 -22
  60. openenv-0.2.1/src/openenv/core/env_server/http_server.py +0 -1076
  61. openenv-0.2.1/src/openenv/core/env_server/mcp_environment.py +0 -386
  62. openenv-0.2.1/src/openenv/core/env_server/mcp_types.py +0 -166
  63. openenv-0.2.1/src/openenv/core/env_server/web_interface.py +0 -1658
  64. openenv-0.2.1/src/openenv/core/mcp_client.py +0 -292
  65. openenv-0.2.1/src/openenv/core/utils.py +0 -27
  66. {openenv-0.2.1 → openenv-0.2.2}/LICENSE +0 -0
  67. {openenv-0.2.1 → openenv-0.2.2}/setup.cfg +0 -0
  68. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/auto/__init__.py +0 -0
  69. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/__init__.py +0 -0
  70. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/commands/build.py +1 -1
  71. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/commands/serve.py +0 -0
  72. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/__init__.py +0 -0
  73. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/README.md +0 -0
  74. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/__init__.py +0 -0
  75. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/openenv.yaml +0 -0
  76. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/server/Dockerfile +0 -0
  77. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/server/__init__.py +0 -0
  78. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/cli/templates/openenv_env/server/requirements.txt +0 -0
  79. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/client_types.py +0 -0
  80. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/containers/__init__.py +0 -0
  81. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/containers/runtime/__init__.py +0 -0
  82. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/containers/runtime/uv_provider.py +0 -0
  83. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/base_transforms.py +0 -0
  84. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/exceptions.py +0 -0
  85. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/env_server/route_config.py +0 -0
  86. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/generic_client.py +0 -0
  87. {openenv-0.2.1 → openenv-0.2.2}/src/openenv/core/tools/git_server_client.py +0 -0
  88. {openenv-0.2.1 → openenv-0.2.2}/src/openenv.egg-info/dependency_links.txt +0 -0
  89. {openenv-0.2.1 → openenv-0.2.2}/src/openenv.egg-info/entry_points.txt +0 -0
  90. {openenv-0.2.1 → openenv-0.2.2}/src/openenv.egg-info/top_level.txt +0 -0
  91. {openenv-0.2.1 → openenv-0.2.2}/tests/test_line_endings.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openenv
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: A unified framework for reinforcement learning environments
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -17,7 +17,9 @@ Requires-Dist: openai>=2.7.2
17
17
  Requires-Dist: tomli>=2.3.0
18
18
  Requires-Dist: tomli-w>=1.2.0
19
19
  Requires-Dist: websockets>=15.0.1
20
- Requires-Dist: fastmcp>=2.0.0
20
+ Requires-Dist: fastmcp>=3.0.0
21
+ Requires-Dist: gradio>=4.0.0
22
+ Requires-Dist: httpx>=0.28.1
21
23
  Provides-Extra: core
22
24
  Requires-Dist: fastapi>=0.104.0; extra == "core"
23
25
  Requires-Dist: pydantic>=2.0.0; extra == "core"
@@ -32,9 +34,28 @@ Requires-Dist: huggingface_hub>=0.20.0; extra == "cli"
32
34
  Requires-Dist: openai>=2.7.2; extra == "cli"
33
35
  Requires-Dist: tomli>=2.3.0; extra == "cli"
34
36
  Requires-Dist: tomli-w>=1.2.0; extra == "cli"
37
+ Provides-Extra: docs
38
+ Requires-Dist: sphinx==7.2.6; extra == "docs"
39
+ Requires-Dist: pytorch-sphinx-theme2; extra == "docs"
40
+ Requires-Dist: sphinxcontrib.katex==0.9.10; extra == "docs"
41
+ Requires-Dist: docutils<0.21,>=0.18.1; extra == "docs"
42
+ Requires-Dist: sphinx-design==0.6.1; extra == "docs"
43
+ Requires-Dist: sphinxcontrib-mermaid==1.0.0; extra == "docs"
44
+ Requires-Dist: myst-parser; extra == "docs"
45
+ Requires-Dist: sphinxext-opengraph; extra == "docs"
46
+ Requires-Dist: sphinx-sitemap==2.7.1; extra == "docs"
47
+ Requires-Dist: sphinx-gallery>=0.14.0; extra == "docs"
48
+ Requires-Dist: matplotlib; extra == "docs"
49
+ Requires-Dist: nest-asyncio; extra == "docs"
50
+ Requires-Dist: smolagents; extra == "docs"
35
51
  Provides-Extra: all
36
52
  Requires-Dist: openenv[core]; extra == "all"
37
53
  Requires-Dist: openenv[cli]; extra == "all"
54
+ Provides-Extra: daytona
55
+ Requires-Dist: daytona>=0.136.0; extra == "daytona"
56
+ Requires-Dist: pyyaml>=6.0; extra == "daytona"
57
+ Provides-Extra: inspect
58
+ Requires-Dist: inspect-ai>=0.3.0; extra == "inspect"
38
59
  Dynamic: license-file
39
60
 
40
61
  # <img width="35" height="35" alt="image" src="https://github.com/user-attachments/assets/2700a971-e5d6-4036-b03f-2f89c9791609" /> OpenEnv: Agentic Execution Environments
@@ -50,7 +71,7 @@ An e2e framework for creating, deploying and using isolated execution environmen
50
71
 
51
72
  **🚀 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/)
52
73
 
53
- **🔥 GPU Mode Tutorial:** End to end tutorial from [GPU Mode](gpu-mode-tutorial/README.md) blog post.
74
+ **🔥 Zero to Hero Tutorial:** End to end tutorial from our [GPU Mode](tutorial/README.md) lecture and other hackathons.
54
75
 
55
76
  ## Quick Start
56
77
 
@@ -69,22 +90,34 @@ pip install git+https://huggingface.co/spaces/openenv/echo_env
69
90
  Then use the environment:
70
91
 
71
92
  ```python
93
+ import asyncio
72
94
  from echo_env import EchoAction, EchoEnv
73
95
 
74
- # Connect to a running Space
75
- client = EchoEnv(base_url="https://openenv-echo-env.hf.space")
96
+ async def main():
97
+ # Connect to a running Space (async context manager)
98
+ async with EchoEnv(base_url="https://openenv-echo-env.hf.space") as client:
99
+ # Reset the environment
100
+ result = await client.reset()
101
+ print(result.observation.echoed_message) # "Echo environment ready!"
76
102
 
77
- # Reset the environment
78
- result = client.reset()
79
- print(result.observation.echoed_message) # "Echo environment ready!"
103
+ # Send messages
104
+ result = await client.step(EchoAction(message="Hello, World!"))
105
+ print(result.observation.echoed_message) # "Hello, World!"
106
+ print(result.reward) # 1.3 (based on message length)
80
107
 
81
- # Send messages
82
- result = client.step(EchoAction(message="Hello, World!"))
83
- print(result.observation.echoed_message) # "Hello, World!"
84
- print(result.reward) # 1.3 (based on message length)
108
+ asyncio.run(main())
109
+ ```
110
+
111
+ **Synchronous usage** is also supported via the `.sync()` wrapper:
85
112
 
86
- # Cleanup
87
- client.close()
113
+ ```python
114
+ from echo_env import EchoAction, EchoEnv
115
+
116
+ # Use .sync() for synchronous context manager
117
+ with EchoEnv(base_url="https://openenv-echo-env.hf.space").sync() as client:
118
+ result = client.reset()
119
+ result = client.step(EchoAction(message="Hello, World!"))
120
+ print(result.observation.echoed_message)
88
121
  ```
89
122
 
90
123
  For a detailed quick start, check out the [docs page](https://meta-pytorch.org/OpenEnv/quickstart/).
@@ -117,6 +150,10 @@ The OpenEnv CLI (`openenv`) provides commands to initialize new environments and
117
150
  Below is a list of active and historical RFCs for OpenEnv. RFCs are proposals for major changes or features. Please review and contribute!
118
151
 
119
152
  - [RFC 001: Baseline API and Interface Specifications](https://github.com/meta-pytorch/OpenEnv/pull/26)
153
+ - [RFC 002: Discoverability of environment tools by agents](https://github.com/meta-pytorch/OpenEnv/pull/32)
154
+ - [RFC 003: Add MCP (Model Context Protocol) support](https://github.com/meta-pytorch/OpenEnv/pull/224)
155
+ - [RFC 004: Add delayed rewards support for trajectory-based scoring](https://github.com/meta-pytorch/OpenEnv/pull/337)
156
+ - [RFC 005: Agentic Harness Integration](https://github.com/meta-pytorch/OpenEnv/pull/387)
120
157
 
121
158
  ## Architecture
122
159
 
@@ -179,6 +216,8 @@ Base class for implementing environment logic:
179
216
 
180
217
  #### 3. EnvClient (Client-Side)
181
218
  Base class for environment communication:
219
+ - **Async by default**: Use `async with` and `await` for all operations
220
+ - **Sync wrapper**: Call `.sync()` to get a `SyncEnvClient` for synchronous usage
182
221
  - Handles WebSocket connections to environment server
183
222
  - Contains a utility to spin up a docker container locally for the corresponding environment
184
223
  - Type-safe action/observation parsing
@@ -261,9 +300,21 @@ See [`envs/README.md`](envs/README.md) for a complete guide on building environm
261
300
  To use an environment:
262
301
  1. Install the client: `pip install git+https://huggingface.co/spaces/openenv/echo-env`
263
302
  2. Import: `from echo_env import EchoAction, EchoEnv`
264
- 3. Create client: `client = EchoEnv(base_url="https://openenv-echo-env.hf.space")`
265
- 4. Interact: `client.reset()`, `client.step(action)`, `client.state()`
266
- 5. Cleanup: `client.close()`
303
+ 3. Use async (recommended) or sync API:
304
+
305
+ **Async (recommended):**
306
+ ```python
307
+ async with EchoEnv(base_url="...") as client:
308
+ result = await client.reset()
309
+ result = await client.step(action)
310
+ ```
311
+
312
+ **Sync (via `.sync()` wrapper):**
313
+ ```python
314
+ with EchoEnv(base_url="...").sync() as client:
315
+ result = client.reset()
316
+ result = client.step(action)
317
+ ```
267
318
 
268
319
  See example scripts in `examples/` directory.
269
320
 
@@ -368,22 +419,15 @@ See the [Oumi example](https://github.com/oumi-ai/oumi/blob/main/notebooks/Oumi%
368
419
 
369
420
  ## Example Environments
370
421
 
371
- ### Echo Environment
372
- A simple environment that echoes back messages with metadata. Perfect for:
373
- - Testing the HTTP server infrastructure
374
- - Learning the framework basics
375
- - Verifying container deployment
376
-
377
- See: [`envs/echo_env/README.md`](envs/echo_env/README.md)
378
-
379
- ### Coding Environment
380
- Executes arbitrary Python code in a sandboxed environment. Features:
381
- - Safe code execution using smolagents
382
- - Capture stdout, stderr, and exit codes
383
- - Persistent execution context within episodes
384
- - Error handling with detailed messages
422
+ | Environment | Description |
423
+ |---|---|
424
+ | [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. |
425
+ | [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. |
426
+ | [Chess Environment](envs/chess_env/README.md) | Chess RL environment with configurable opponents and full rules support. |
427
+ | [Atari Environment](envs/atari_env/README.md) | Classic Arcade Learning Environment tasks for RL benchmarking. |
428
+ | [FinRL Environment](envs/finrl_env/README.md) | Financial market simulations for algorithmic trading experiments. |
385
429
 
386
- See: [`envs/coding_env/README.md`](envs/coding_env/README.md)
430
+ > Browse the full catalog of community environments at [meta-pytorch.org/OpenEnv/environments](https://meta-pytorch.org/OpenEnv/environments/).
387
431
 
388
432
  ## Community Support & Acknowledgments
389
433
  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!!
@@ -1,42 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: openenv
3
- Version: 0.2.1
4
- Summary: A unified framework for reinforcement learning environments
5
- Requires-Python: >=3.10
6
- Description-Content-Type: text/markdown
7
- License-File: LICENSE
8
- Requires-Dist: fastapi>=0.104.0
9
- Requires-Dist: pydantic>=2.0.0
10
- Requires-Dist: uvicorn>=0.24.0
11
- Requires-Dist: requests>=2.25.0
12
- Requires-Dist: typer>=0.9.0
13
- Requires-Dist: rich>=13.0.0
14
- Requires-Dist: pyyaml>=6.0
15
- Requires-Dist: huggingface_hub>=0.20.0
16
- Requires-Dist: openai>=2.7.2
17
- Requires-Dist: tomli>=2.3.0
18
- Requires-Dist: tomli-w>=1.2.0
19
- Requires-Dist: websockets>=15.0.1
20
- Requires-Dist: fastmcp>=2.0.0
21
- Provides-Extra: core
22
- Requires-Dist: fastapi>=0.104.0; extra == "core"
23
- Requires-Dist: pydantic>=2.0.0; extra == "core"
24
- Requires-Dist: uvicorn>=0.24.0; extra == "core"
25
- Requires-Dist: requests>=2.25.0; extra == "core"
26
- Requires-Dist: websockets>=15.0.1; extra == "core"
27
- Provides-Extra: cli
28
- Requires-Dist: typer>=0.9.0; extra == "cli"
29
- Requires-Dist: rich>=13.0.0; extra == "cli"
30
- Requires-Dist: pyyaml>=6.0; extra == "cli"
31
- Requires-Dist: huggingface_hub>=0.20.0; extra == "cli"
32
- Requires-Dist: openai>=2.7.2; extra == "cli"
33
- Requires-Dist: tomli>=2.3.0; extra == "cli"
34
- Requires-Dist: tomli-w>=1.2.0; extra == "cli"
35
- Provides-Extra: all
36
- Requires-Dist: openenv[core]; extra == "all"
37
- Requires-Dist: openenv[cli]; extra == "all"
38
- Dynamic: license-file
39
-
40
1
  # <img width="35" height="35" alt="image" src="https://github.com/user-attachments/assets/2700a971-e5d6-4036-b03f-2f89c9791609" /> OpenEnv: Agentic Execution Environments
41
2
 
42
3
  An e2e framework for creating, deploying and using isolated execution environments for agentic RL training, built using Gymnasium style simple APIs.
@@ -50,7 +11,7 @@ An e2e framework for creating, deploying and using isolated execution environmen
50
11
 
51
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/)
52
13
 
53
- **🔥 GPU Mode Tutorial:** End to end tutorial from [GPU Mode](gpu-mode-tutorial/README.md) blog post.
14
+ **🔥 Zero to Hero Tutorial:** End to end tutorial from our [GPU Mode](tutorial/README.md) lecture and other hackathons.
54
15
 
55
16
  ## Quick Start
56
17
 
@@ -69,22 +30,34 @@ pip install git+https://huggingface.co/spaces/openenv/echo_env
69
30
  Then use the environment:
70
31
 
71
32
  ```python
33
+ import asyncio
72
34
  from echo_env import EchoAction, EchoEnv
73
35
 
74
- # Connect to a running Space
75
- client = EchoEnv(base_url="https://openenv-echo-env.hf.space")
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)
76
47
 
77
- # Reset the environment
78
- result = client.reset()
79
- print(result.observation.echoed_message) # "Echo environment ready!"
48
+ asyncio.run(main())
49
+ ```
80
50
 
81
- # Send messages
82
- result = client.step(EchoAction(message="Hello, World!"))
83
- print(result.observation.echoed_message) # "Hello, World!"
84
- print(result.reward) # 1.3 (based on message length)
51
+ **Synchronous usage** is also supported via the `.sync()` wrapper:
85
52
 
86
- # Cleanup
87
- client.close()
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)
88
61
  ```
89
62
 
90
63
  For a detailed quick start, check out the [docs page](https://meta-pytorch.org/OpenEnv/quickstart/).
@@ -117,6 +90,10 @@ The OpenEnv CLI (`openenv`) provides commands to initialize new environments and
117
90
  Below is a list of active and historical RFCs for OpenEnv. RFCs are proposals for major changes or features. Please review and contribute!
118
91
 
119
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)
120
97
 
121
98
  ## Architecture
122
99
 
@@ -179,6 +156,8 @@ Base class for implementing environment logic:
179
156
 
180
157
  #### 3. EnvClient (Client-Side)
181
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
182
161
  - Handles WebSocket connections to environment server
183
162
  - Contains a utility to spin up a docker container locally for the corresponding environment
184
163
  - Type-safe action/observation parsing
@@ -261,9 +240,21 @@ See [`envs/README.md`](envs/README.md) for a complete guide on building environm
261
240
  To use an environment:
262
241
  1. Install the client: `pip install git+https://huggingface.co/spaces/openenv/echo-env`
263
242
  2. Import: `from echo_env import EchoAction, EchoEnv`
264
- 3. Create client: `client = EchoEnv(base_url="https://openenv-echo-env.hf.space")`
265
- 4. Interact: `client.reset()`, `client.step(action)`, `client.state()`
266
- 5. Cleanup: `client.close()`
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
+ ```
267
258
 
268
259
  See example scripts in `examples/` directory.
269
260
 
@@ -368,22 +359,15 @@ See the [Oumi example](https://github.com/oumi-ai/oumi/blob/main/notebooks/Oumi%
368
359
 
369
360
  ## Example Environments
370
361
 
371
- ### Echo Environment
372
- A simple environment that echoes back messages with metadata. Perfect for:
373
- - Testing the HTTP server infrastructure
374
- - Learning the framework basics
375
- - Verifying container deployment
376
-
377
- See: [`envs/echo_env/README.md`](envs/echo_env/README.md)
378
-
379
- ### Coding Environment
380
- Executes arbitrary Python code in a sandboxed environment. Features:
381
- - Safe code execution using smolagents
382
- - Capture stdout, stderr, and exit codes
383
- - Persistent execution context within episodes
384
- - Error handling with detailed messages
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. |
385
369
 
386
- See: [`envs/coding_env/README.md`](envs/coding_env/README.md)
370
+ > Browse the full catalog of community environments at [meta-pytorch.org/OpenEnv/environments](https://meta-pytorch.org/OpenEnv/environments/).
387
371
 
388
372
  ## Community Support & Acknowledgments
389
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!!
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openenv"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "A unified framework for reinforcement learning environments"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -26,7 +26,10 @@ dependencies = [
26
26
  "tomli-w>=1.2.0",
27
27
  "websockets>=15.0.1",
28
28
  # MCP support
29
- "fastmcp>=2.0.0",
29
+ "fastmcp>=3.0.0",
30
+ # Web UI dependencies
31
+ "gradio>=4.0.0",
32
+ "httpx>=0.28.1",
30
33
  ]
31
34
 
32
35
  [project.optional-dependencies]
@@ -46,10 +49,32 @@ cli = [
46
49
  "tomli>=2.3.0",
47
50
  "tomli-w>=1.2.0",
48
51
  ]
52
+ docs = [
53
+ "sphinx==7.2.6",
54
+ "pytorch-sphinx-theme2",
55
+ "sphinxcontrib.katex==0.9.10",
56
+ "docutils>=0.18.1,<0.21",
57
+ "sphinx-design==0.6.1",
58
+ "sphinxcontrib-mermaid==1.0.0",
59
+ "myst-parser",
60
+ "sphinxext-opengraph",
61
+ "sphinx-sitemap==2.7.1",
62
+ "sphinx-gallery>=0.14.0",
63
+ "matplotlib",
64
+ "nest-asyncio",
65
+ "smolagents",
66
+ ]
49
67
  all = [
50
68
  "openenv[core]",
51
69
  "openenv[cli]",
52
70
  ]
71
+ daytona = [
72
+ "daytona>=0.136.0",
73
+ "pyyaml>=6.0",
74
+ ]
75
+ inspect = [
76
+ "inspect-ai>=0.3.0",
77
+ ]
53
78
 
54
79
  [project.scripts]
55
80
  openenv = "openenv.cli.__main__:main"
@@ -82,8 +107,42 @@ exclude_lines = [
82
107
  ]
83
108
 
84
109
  [tool.pytest.ini_options]
110
+ asyncio_mode = "auto"
111
+ asyncio_default_fixture_loop_scope = "function"
85
112
  markers = [
86
113
  "docker: Tests that require Docker to be running",
87
114
  "network: Tests that require network access (HuggingFace, etc.)",
88
115
  "integration: Integration tests with external resources",
89
116
  ]
117
+
118
+ [dependency-groups]
119
+ dev = [
120
+ "ruff>=0.14.0",
121
+ "usort>=1.1.0",
122
+ "pytest>=7.0",
123
+ "pytest-asyncio>=0.21",
124
+ ]
125
+
126
+ [tool.usort]
127
+ # Disable first_party auto-detection so all non-stdlib imports land in
128
+ # the same "third_party" bucket (the default_category). This matches
129
+ # pyfmt's usort behavior inside arc f, which groups openenv.* and env
130
+ # package imports together without blank-line separators.
131
+ first_party_detection = false
132
+
133
+ [tool.ruff]
134
+ line-length = 88
135
+
136
+ [tool.ruff.lint]
137
+ select = ["E", "F", "W"]
138
+ ignore = [
139
+ "E402", # Module level import not at top of file (needed for pytest.importorskip patterns)
140
+ "E501", # Line too long (not enforced previously, would require large refactor)
141
+ ]
142
+
143
+ [tool.ruff.lint.per-file-ignores]
144
+ # Context manager variables that are intentionally unused
145
+ "tests/envs/test_websockets.py" = ["F841"]
146
+ "tests/test_cli/test_push.py" = ["F841"]
147
+ # Compatibility shim module
148
+ "src/openenv_core/__init__.py" = ["F401"]
@@ -0,0 +1,54 @@
1
+ """Unified OpenEnv package bundling the CLI and core runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module, metadata
6
+
7
+ __all__ = [
8
+ "core",
9
+ "cli",
10
+ "AutoEnv",
11
+ "AutoAction",
12
+ "GenericEnvClient",
13
+ "GenericAction",
14
+ "SyncEnvClient",
15
+ ]
16
+
17
+ try:
18
+ __version__ = metadata.version("openenv") # type: ignore[arg-type]
19
+ except metadata.PackageNotFoundError: # pragma: no cover - local dev
20
+ __version__ = "0.0.0"
21
+
22
+
23
+ _LAZY_MODULES = {
24
+ "core": ".core",
25
+ "cli": ".cli",
26
+ }
27
+
28
+ _LAZY_ATTRS = {
29
+ "AutoEnv": (".auto", "AutoEnv"),
30
+ "AutoAction": (".auto", "AutoAction"),
31
+ "GenericEnvClient": (".core", "GenericEnvClient"),
32
+ "GenericAction": (".core", "GenericAction"),
33
+ "SyncEnvClient": (".core", "SyncEnvClient"),
34
+ }
35
+
36
+
37
+ def __getattr__(name: str):
38
+ if name in _LAZY_MODULES:
39
+ module = import_module(_LAZY_MODULES[name], __name__)
40
+ globals()[name] = module
41
+ return module
42
+
43
+ if name in _LAZY_ATTRS:
44
+ module_path, attr_name = _LAZY_ATTRS[name]
45
+ module = import_module(module_path, __name__)
46
+ value = getattr(module, attr_name)
47
+ globals()[name] = value
48
+ return value
49
+
50
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
51
+
52
+
53
+ def __dir__() -> list[str]:
54
+ return sorted(set(globals().keys()) | set(__all__))
@@ -24,9 +24,9 @@ import json
24
24
  import logging
25
25
  import re
26
26
  import tempfile
27
- from dataclasses import dataclass, asdict
27
+ from dataclasses import asdict, dataclass
28
28
  from pathlib import Path
29
- from typing import Dict, List, Optional, Type, Any
29
+ from typing import Any, Dict, Optional, Type
30
30
 
31
31
  import yaml
32
32
 
@@ -33,9 +33,9 @@ Example:
33
33
  from __future__ import annotations
34
34
 
35
35
  import logging
36
- from typing import Type, Dict, Any
36
+ from typing import Any, Dict, Type
37
37
 
38
- from ._discovery import get_discovery, _is_hub_url
38
+ from ._discovery import _is_hub_url, get_discovery
39
39
  from .auto_env import AutoEnv
40
40
 
41
41
  logger = logging.getLogger(__name__)
@@ -153,9 +153,9 @@ class AutoAction:
153
153
 
154
154
  if not available_envs:
155
155
  raise ValueError(
156
- f"No OpenEnv environments found.\n"
157
- f"Install an environment with: pip install openenv-<env-name>\n"
158
- f"Or specify a HuggingFace Hub repository: AutoAction.from_env('openenv/echo_env')"
156
+ "No OpenEnv environments found.\n"
157
+ "Install an environment with: pip install openenv-<env-name>\n"
158
+ "Or specify a HuggingFace Hub repository: AutoAction.from_env('openenv/echo_env')"
159
159
  )
160
160
 
161
161
  # Try to suggest similar environment names
@@ -36,10 +36,13 @@ import os
36
36
  import shutil
37
37
  import subprocess
38
38
  import sys
39
+ from typing import Any, Dict, Optional, TYPE_CHECKING
40
+
39
41
  import requests
40
- from typing import Any, Optional, TYPE_CHECKING, Dict
42
+ from openenv.core.utils import run_async_safely
43
+
44
+ from ._discovery import _is_hub_url, get_discovery
41
45
 
42
- from ._discovery import get_discovery, _is_hub_url
43
46
 
44
47
  if TYPE_CHECKING:
45
48
  from openenv.core.containers.runtime import ContainerProvider
@@ -590,12 +593,14 @@ class AutoEnv:
590
593
  f"Space not running at {space_url}, "
591
594
  f"using GenericEnvClient with HF Docker registry"
592
595
  )
593
- return GenericEnvClient.from_env(
594
- name,
595
- use_docker=True,
596
- provider=container_provider,
597
- env_vars=env_vars or {},
598
- **kwargs,
596
+ return run_async_safely(
597
+ GenericEnvClient.from_env(
598
+ name,
599
+ use_docker=True,
600
+ provider=container_provider,
601
+ env_vars=env_vars or {},
602
+ **kwargs,
603
+ )
599
604
  )
600
605
 
601
606
  # For local environments with skip_install, we need docker_image
@@ -604,12 +609,14 @@ class AutoEnv:
604
609
  f"Using GenericEnvClient with Docker image {docker_image} "
605
610
  f"(skip_install=True)"
606
611
  )
607
- return GenericEnvClient.from_docker_image(
608
- image=docker_image,
609
- provider=container_provider,
610
- wait_timeout=wait_timeout,
611
- env_vars=env_vars or {},
612
- **kwargs,
612
+ return run_async_safely(
613
+ GenericEnvClient.from_docker_image(
614
+ image=docker_image,
615
+ provider=container_provider,
616
+ wait_timeout=wait_timeout,
617
+ env_vars=env_vars or {},
618
+ **kwargs,
619
+ )
613
620
  )
614
621
  else:
615
622
  raise ValueError(
@@ -715,12 +722,14 @@ class AutoEnv:
715
722
  # Local server not running, auto-start Docker container
716
723
  logger.info(f"❌ Server not available at {base_url}")
717
724
  logger.info(f"🐳 Auto-starting Docker container: {docker_image}")
718
- return client_class.from_docker_image(
719
- image=docker_image,
720
- provider=container_provider,
721
- wait_timeout=wait_timeout,
722
- env_vars=env_vars or {},
723
- **kwargs,
725
+ return run_async_safely(
726
+ client_class.from_docker_image(
727
+ image=docker_image,
728
+ provider=container_provider,
729
+ wait_timeout=wait_timeout,
730
+ env_vars=env_vars or {},
731
+ **kwargs,
732
+ )
724
733
  )
725
734
  else:
726
735
  # Remote server not available, cannot auto-start
@@ -730,12 +739,14 @@ class AutoEnv:
730
739
  )
731
740
  else:
732
741
  # No base_url provided, start new Docker container
733
- return client_class.from_docker_image(
734
- image=docker_image,
735
- provider=container_provider,
736
- wait_timeout=wait_timeout,
737
- env_vars=env_vars or {},
738
- **kwargs,
742
+ return run_async_safely(
743
+ client_class.from_docker_image(
744
+ image=docker_image,
745
+ provider=container_provider,
746
+ wait_timeout=wait_timeout,
747
+ env_vars=env_vars or {},
748
+ **kwargs,
749
+ )
739
750
  )
740
751
  except Exception as e:
741
752
  raise ValueError(
@@ -14,8 +14,7 @@ following the Hugging Face CLI pattern.
14
14
  import sys
15
15
 
16
16
  import typer
17
-
18
- from openenv.cli.commands import build, init, push, serve, validate
17
+ from openenv.cli.commands import build, fork, init, push, serve, skills, validate
19
18
 
20
19
  # Create the main CLI app
21
20
  app = typer.Typer(
@@ -39,6 +38,15 @@ app.command(
39
38
  app.command(name="serve", help="Serve environments locally (TODO: Phase 4)")(
40
39
  serve.serve
41
40
  )
41
+ app.command(
42
+ name="fork",
43
+ help="Fork (duplicate) a Hugging Face Space to your account",
44
+ )(fork.fork)
45
+ app.add_typer(
46
+ skills.app,
47
+ name="skills",
48
+ help="Manage OpenEnv skills for AI assistants",
49
+ )
42
50
 
43
51
 
44
52
  # Entry point for setuptools