Skip to content

Conversation

@wukaixingxp
Copy link
Contributor

@wukaixingxp wukaixingxp commented Nov 20, 2025

AutoEnv & AutoAction: Auto-Discovery System for OpenEnv

Overview

The auto-discovery system provides two main classes:

  • AutoEnv: Automatically loads and instantiates environment clients
  • AutoAction: Automatically loads action classes for environments

Both classes work with:

  • Local packages: Installed via pip install openenv-<env-name>
  • HuggingFace Hub: Environments hosted on HuggingFace Spaces

Quick Start

Basic Usage

Instead of manually importing specific environment classes:

# Old way
from envs.coding_env import CodingEnv, CodeAction
env = CodingEnv.from_docker_image("coding-env:latest")

You can now use the auto-discovery API:

# New way
from openenv import AutoEnv, AutoAction

# Create environment
env = AutoEnv.from_env("coding-env")

# Get action class
CodeAction = AutoAction.from_env("coding-env")

# Use them together
result = env.reset()
action = CodeAction(code="print('Hello, OpenEnv!')")
step_result = env.step(action)
env.close()

AutoEnv API

AutoEnv.from_env(name, **kwargs)

Create an environment client from a name or HuggingFace Hub repository.

Parameters:

  • name: Environment name or Hub repo ID
    • Local: "coding", "coding-env", "coding_env"
    • Hub: "meta-pytorch/coding-env", "username/env-name"
  • base_url: Optional base URL for HTTP connection
  • docker_image: Optional Docker image name (overrides default)
  • container_provider: Optional container provider
  • wait_timeout: Timeout for container startup (default: 30s)
  • env_vars: Optional environment variables for the container
  • **kwargs: Additional arguments passed to the client class

Returns: Instance of the environment client class

Examples:

from openenv import AutoEnv

# From installed package
env = AutoEnv.from_env("coding-env")

# From HuggingFace Hub
env = AutoEnv.from_env("meta-pytorch/coding-env")

# With custom configuration
env = AutoEnv.from_env(
    "coding",
    docker_image="my-coding-env:v2",
    wait_timeout=60.0,
    env_vars={"DEBUG": "1"}
)

AutoEnv.list_environments()

List all available environments.

from openenv import AutoEnv

AutoEnv.list_environments()
# Output:
# Available Environments:
# ----------------------------------------------------------------------
# coding         : Coding environment for OpenEnv (v0.1.0)
# echo           : echo_env environment (v0.1.0)
# browsergym     : BrowserGym environment (v0.1.0)
# ...

AutoEnv.get_env_info(name)

Get detailed information about an environment.

from openenv import AutoEnv

info = AutoEnv.get_env_info("coding")
print(f"Description: {info['description']}")
print(f"Version: {info['version']}")
print(f"Docker Image: {info['default_image']}")
print(f"Client Class: {info['env_class']}")
print(f"Action Class: {info['action_class']}")

AutoEnv.get_env_class(name)

Get the environment class (not an instance).

from openenv import AutoEnv

CodingEnv = AutoEnv.get_env_class("coding")
# Now you can instantiate it yourself with custom parameters
env = CodingEnv.from_docker_image("coding-env:latest", wait_timeout=60.0)

AutoAction API

AutoAction.from_env(name)

Get the Action class from an environment name or HuggingFace Hub repository.

Parameters:

  • name: Environment name or Hub repo ID

Returns: Action class (not an instance!)

Examples:

from openenv import AutoAction

# From installed package
CodeAction = AutoAction.from_env("coding-env")
action = CodeAction(code="print('Hello!')")

# From HuggingFace Hub
CodeAction = AutoAction.from_env("meta-pytorch/coding-env")

# Different name formats work
EchoAction = AutoAction.from_env("echo")
EchoAction = AutoAction.from_env("echo-env")
EchoAction = AutoAction.from_env("echo_env")

AutoAction.from_hub(env_name)

Alias for from_env() for backward compatibility.

from openenv import AutoAction

CodeAction = AutoAction.from_env("coding")
action = CodeAction(code="x = 5 + 3")

AutoAction.list_actions()

List all available action classes.

from openenv import AutoAction

AutoAction.list_actions()
# Output:
# Available Action Classes:
# ----------------------------------------------------------------------
# coding         : CodeAction
# echo           : EchoAction
# browsergym     : BrowsergymAction
# ...

AutoAction.get_action_info(name)

Get detailed information about an action class.

from openenv import AutoAction

info = AutoAction.get_action_info("coding")
print(f"Action Class: {info['action_class']}")
print(f"Module: {info['module']}")

HuggingFace Hub Integration

Loading from HuggingFace Spaces

AutoEnv can automatically connect to environments running on HuggingFace Spaces:

from openenv import AutoEnv, AutoAction

# Load from HuggingFace Space
env = AutoEnv.from_env("username/coding-env-test")

# Get action class
CodeAction = AutoAction.from_env("username/coding-env-test")

# Use normally
result = env.reset()
action = CodeAction(code="print('Hello from HF Space!')")
step_result = env.step(action)

print(f"Output: {step_result.observation.stdout}")
env.close()

The system automatically:

  1. Detects HuggingFace repo IDs (format: username/repo-name)
  2. Resolves the Space URL (e.g., https://username-repo-name.hf.space)
  3. Checks if the Space is running and accessible
  4. Downloads the environment package if needed
  5. Connects to the running Space

Complete Workflow Example

Here's a complete example showing the auto-discovery workflow:

from openenv import AutoEnv, AutoAction

# 1. List available environments
print("Available environments:")
AutoEnv.list_environments()

# 2. Create environment
env = AutoEnv.from_env("coding-env")

# 3. Get action class
CodeAction = AutoAction.from_env("coding-env")

# 4. Run environment
result = env.reset()
print(f"Environment ready: {result.observation}")

# 5. Execute actions
action = CodeAction(code="""
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(f"Fibonacci(10) = {fibonacci(10)}")
""")

step_result = env.step(action)
print(f"Output:\n{step_result.observation.stdout}")

# 6. Clean up
env.close()

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 20, 2025
@wukaixingxp wukaixingxp marked this pull request as ready for review November 20, 2025 20:34
@wukaixingxp
Copy link
Contributor Author

I just noticed that there is some file in src/envs/coding_env got changed.. l can revert it back since I am planing to get a PR to improve the coding env as well.

@jspisak jspisak requested a review from burtenshaw November 20, 2025 21:13
@burtenshaw
Copy link
Collaborator

Very cool PR @wukaixingxp ! There are a few things that concern me, which I think we should discuss / solve at a high level before going into the details.

  • This implementation makes extra changes in core and envs which I don't think we need for this problem. We should keep this PR focused because it's already already a big feature.
  • The implementation relies on the envs' servers and clients being in src/envs. However, this is not a given. In fact envs can live on registries, the hugging face hub, local docker runtime, and soon more. Both AutoEnv and Auto* client methods should be able to find the correct classes without relying on src/envs. It might be worth solving the problem for one situation first, like the huggingface hub, first before moving on to every situation. The hub already contain installable client code, docker images, and a running server. Check out these docs to understand further how envs are made and shared.
  • src/envs/_manifest.py will become hard to maintain as envs get into the hundreds and thousands. Therefore, I would rely on env.yaml to define the class name, locations of classes, and versions, then retrieve that code based on the definition.
  • Also, you'll notice from the template env pyproject.yaml that envs use a consistent naming convention to install their client python packages, i.e. openenv-__ENV_NAME__. We could define or parse this from openenv.yml and look for the correct classes within that python package. If the package is missing, we could simply raise and tell the user to install the package. This should decouple the client side from src/envs nicely.

Just an idea, but due to the challenges above. It might be best to solve this problem on the client side first, before moving on to the server side. In principle, a declared env like EchoEnv should work with automated client classes like AutoAction, and I think that would have more impact on DX than AutoEnv.

@wukaixingxp
Copy link
Contributor Author

wukaixingxp commented Nov 22, 2025

  • huggingface hub,

Thanks for your feedback! It is really helpful! I will remove the _manifest and use the openenv-__ENV_NAME__ instead.. Let me also test the hub url from huggingface space as well.

@burtenshaw
Copy link
Collaborator

burtenshaw commented Nov 22, 2025

Thanks for your feedback! It is really helpful! I will remove the _manifest and use the openenv-__ENV_NAME__ instead.. Let me also test the hub url from huggingface space as well.

@wukaixingxp In general, it's best to interface with hub repos via their repo_id. i.e. <user>/<space-name> and construct the URL where needed. The openenv cli already does this along with all major libraries.

@wukaixingxp
Copy link
Contributor Author

wukaixingxp commented Dec 2, 2025

@burtenshaw do you want to take another look? I managed to get HF space working and added some test cases

from envs import AutoEnv, AutoAction

# Load from HuggingFace Space using repo ID
env = AutoEnv.from_name("wukaixingxp/coding-env-test")

# Reset environment
observation = env.reset()
print(f"Reset observation: {observation}")

# Get action class
CodeAction = AutoAction.from_name("wukaixingxp/coding-env-test")

# Create and execute action
action = CodeAction(code="print('Hello!')")
result = env.step(action)  # Returns StepResult object, not tuple

# Access result properties
print(f"Observation: {result.observation}")
print(f"Reward: {result.reward}")
print(f"Done: {result.done}")

# Clean up
env.close()

logs:

Fetching 15 files: 100%|███████| 15/15 [00:00<00:00, 1879.56it/s]
Reset observation: StepResult(observation=CodeObservation(done=False, reward=None, metadata={}, stdout='', stderr='', exit_code=0), reward=0.0, done=False)
Observation: CodeObservation(done=False, reward=None, metadata={}, stdout='Hello!\n', stderr='', exit_code=0)
Reward: 0.0
Done: False

Copy link
Collaborator

@burtenshaw burtenshaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wukaixingxp Nice work! This is looking much closer. I've done a first high level review on the structure. Mainly just to bring it inline with #232 and decouple this feature from src/envs completely.

This is the high level results, but I've also commented on files to clarify:

  • merge release for the structural changes from #232 so that the import can be from openenv import AutoWhatever.
  • create a module in src/openenv/auto and expose the auto classes at the library root so the above works.
  • rename from_name to from_hub. This is more consistent and will explain to users what they are calling.

Let me know if you need a hand with merging #232.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this from the PR and add an md file in docs that explains how to use it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This directory will be removed in #232 .

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After merging #232 this will need to reside at src/openenv/auto/

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, after merging #232 this will need to reside at src/openenv/auto

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, after merging #232 this will need to reside at src/openenv/auto

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this change?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice.

@wukaixingxp
Copy link
Contributor Author

@burtenshaw sure.. I wonder if the #232 will be merged to main soon so I can rebase my change to that?

@burtenshaw
Copy link
Collaborator

@burtenshaw sure.. I wonder if the #232 will be merged to main soon so I can rebase my change to that?

Yeah. That would be great. We're waiting for reviews and targeting #239 in the meantime.

@wukaixingxp
Copy link
Contributor Author

@burtenshaw do you want to keep .from_name() function? now I replaced .from_name() with .from_hub(). I created a diff to merge into release branch #245 but I found out that the release branch is not yet up-to-date with main, so my PR is a bit messy. Can you merge the latest main to release branch?

@burtenshaw
Copy link
Collaborator

burtenshaw commented Dec 11, 2025

@wukaixingxp Sure. #232 is now up to date with main.

wukaixingxp and others added 6 commits December 19, 2025 17:39
Add HuggingFace-style auto-discovery system for environments:

- AutoEnv.from_hub(name): Factory to auto-detect and instantiate environment clients
- AutoAction.from_hub(name): Factory to load action classes for environments
- EnvironmentDiscovery: Package discovery using importlib.metadata and openenv.yaml manifests
- Support for local packages (openenv-*) and HuggingFace Hub

Includes comprehensive tests and documentation.
Adds improved handling for factory functions in load_environment_metadata,
using inspect.isclass/isfunction for more robust type detection.
wukaixingxp and others added 8 commits December 27, 2025 11:55
This commit addresses the failing CI/CD tests in PR meta-pytorch#222 by:

1. **Fix importlib cache invalidation**: Added `importlib.invalidate_caches()`
   call in `_discover_installed_packages()` to ensure newly installed packages
   are detected after runtime pip installs. Previously, when packages were
   installed via `_install_from_path()`, the importlib.metadata cache wasn't
   invalidated, causing discovery to fail.

2. **Fix discovery refresh after install**: In `_ensure_package_from_hub()`,
   added missing `discover(use_cache=False)` call after installing a new
   package. This ensures the discovery system refreshes and picks up the newly
   installed environment.

3. **Skip integration tests in CI**: Updated `.github/workflows/test.yml` to
   skip tests marked with `integration`, `network`, or `docker` markers. These
   tests require external resources (HuggingFace Spaces, Docker daemon) that
   aren't available or reliable in CI environments.

4. **Add pytest markers configuration**: Added `[tool.pytest.ini_options]` to
   `pyproject.toml` with proper marker definitions to eliminate pytest warnings
   and document test categories.

The root cause was that after `pip install -e` at runtime, Python's
importlib.metadata.distributions() iterator didn't pick up the newly installed
package without explicit cache invalidation. This caused AutoEnv.from_hub() to
fail with "No OpenEnv environments found" error in CI.
from __future__ import annotations

from openenv.core.env_server.types import Action, Observation, State
from dataclasses import dataclass
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's best to move this to pydantic whilst we're working on it.

@burtenshaw
Copy link
Collaborator

Hey @wukaixingxp, I've tested the install process from spaces and I think we should take a slightly different approach. At the moment, iiuc, the module downloads the space repo installs with pip. Some issues with this are:

  • downloading is not necessary because the space repo is git compatible. Therefore, we can do: uv pip install git+https://huggingface.co/spaces/burtenshaw/wordle and install the env directly.
  • the implementation relies on pip but much of this library uses uv pip. We should validate that.
  • we're installing remote code, which is a vulnerability. Therefore, we should ask for use confirmation before installing.

Based on these, I would recommend changing the logic to avoid download and use git+, validate remote install, and potentially ask whether to use uv pip|pip`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants