openenv/coding_env
21
1# Copyright (c) Meta Platforms, Inc. and affiliates.2# All rights reserved.3#4# This source code is licensed under the BSD-style license found in the5# LICENSE file in the root directory of this source tree.6 7"""8AutoAction - Automatic Action Class Selection9==============================================10 11AutoAction provides a HuggingFace-style API for automatically retrieving the12correct Action class from installed packages or HuggingFace Hub.13 14This module simplifies working with environment actions by automatically15detecting and returning the appropriate Action class without requiring16manual imports.17 18Example:19 >>> from openenv import AutoEnv, AutoAction20 >>>21 >>> # Get Action class from environment name22 >>> CodeAction = AutoAction.from_env("coding")23 >>> action = CodeAction(code="print('Hello!')")24 >>>25 >>> # From HuggingFace Hub26 >>> CodeAction = AutoAction.from_env("meta-pytorch/coding-env")27 >>>28 >>> # Use with AutoEnv29 >>> env = AutoEnv.from_env("coding-env")30 >>> result = env.step(action)31"""32 33from __future__ import annotations34 35import logging36from typing import Any, Dict, Type37 38from ._discovery import _is_hub_url, get_discovery39from .auto_env import AutoEnv40 41logger = logging.getLogger(__name__)42 43 44class AutoAction:45 """46 AutoAction automatically retrieves the correct Action class based on47 environment names or HuggingFace Hub repositories.48 49 This class follows the HuggingFace AutoModel pattern, making it easy to50 get the right Action class without needing to know which module to import.51 52 The class provides factory methods that look up the Action class and53 return the class (not an instance) for you to instantiate.54 55 Example:56 >>> # From installed package57 >>> CodeAction = AutoAction.from_env("coding")58 >>> action = CodeAction(code="print('test')")59 >>>60 >>> # From HuggingFace Hub61 >>> CodeAction = AutoAction.from_env("meta-pytorch/coding-env")62 >>> action = CodeAction(code="print('test')")63 >>>64 >>> # Use with AutoEnv for a complete workflow65 >>> env = AutoEnv.from_env("coding-env")66 >>> ActionClass = AutoAction.from_env("coding-env")67 >>> action = ActionClass(code="print('Hello, AutoAction!')")68 >>> result = env.step(action)69 70 Note:71 AutoAction is not meant to be instantiated directly. Use the class72 method from_env() instead.73 """74 75 def __init__(self):76 """AutoAction should not be instantiated directly. Use class methods instead."""77 raise TypeError(78 "AutoAction is a factory class and should not be instantiated directly. "79 "Use AutoAction.from_hub() or AutoAction.from_env() instead."80 )81 82 @classmethod83 def from_env(cls, name: str, skip_install: bool = False) -> Type:84 """85 Get the Action class from environment name or HuggingFace Hub repository.86 87 This method automatically:88 1. Checks if the name is a HuggingFace Hub URL/repo ID89 2. If Hub: downloads and installs the environment package90 3. If local: looks up the installed openenv-* package91 4. Imports and returns the Action class92 93 Args:94 name: Environment name or HuggingFace Hub repo ID95 Examples:96 - "coding" / "coding-env" / "coding_env"97 - "meta-pytorch/coding-env" (Hub repo ID)98 - "https://huggingface.co/meta-pytorch/coding-env" (Hub URL)99 skip_install: If True, skip package installation and return100 GenericAction class instead. Use this when working with101 GenericEnvClient to avoid installing remote packages.102 103 Returns:104 Action class (not an instance!). Returns GenericAction when105 skip_install=True.106 107 Raises:108 ValueError: If environment not found (only when skip_install=False)109 ImportError: If environment package is not installed (only when skip_install=False)110 111 Examples:112 >>> # From installed package113 >>> CodeAction = AutoAction.from_env("coding-env")114 >>> action = CodeAction(code="print('Hello!')")115 >>>116 >>> # From HuggingFace Hub117 >>> CodeAction = AutoAction.from_env("meta-pytorch/coding-env")118 >>> action = CodeAction(code="print('Hello!')")119 >>>120 >>> # Skip installation, use GenericAction (for GenericEnvClient)121 >>> ActionClass = AutoAction.from_env("user/repo", skip_install=True)122 >>> action = ActionClass(code="print('Hello!')") # Returns GenericAction123 >>>124 >>> # Different name formats125 >>> EchoAction = AutoAction.from_env("echo")126 >>> EchoAction = AutoAction.from_env("echo-env")127 >>> EchoAction = AutoAction.from_env("echo_env")128 """129 # If skip_install is True, return GenericAction without any package lookup130 if skip_install:131 from openenv.core.generic_client import GenericAction132 133 logger.info(134 f"Returning GenericAction for '{name}' (skip_install=True). "135 f"Use keyword arguments to create actions: GenericAction(code='...')"136 )137 return GenericAction138 139 # Check if it's a HuggingFace Hub URL or repo ID140 if _is_hub_url(name):141 # Ensure package is installed (reuse AutoEnv logic, downloads only if needed)142 env_name = AutoEnv._ensure_package_from_hub(name)143 else:144 env_name = name145 146 # Get environment info from discovery147 discovery = get_discovery()148 env_info = discovery.get_environment_by_name(env_name)149 150 if not env_info:151 # Environment not found - provide helpful error message152 available_envs = discovery.discover()153 154 if not available_envs:155 raise ValueError(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 )160 161 # Try to suggest similar environment names162 from difflib import get_close_matches163 164 env_keys = list(available_envs.keys())165 suggestions = get_close_matches(env_name, env_keys, n=3, cutoff=0.6)166 167 error_msg = f"Unknown environment '{env_name}'.\n"168 if suggestions:169 error_msg += f"Did you mean: {', '.join(suggestions)}?\n"170 error_msg += f"Available environments: {', '.join(sorted(env_keys))}"171 172 raise ValueError(error_msg)173 174 # Get the action class175 try:176 action_class = env_info.get_action_class()177 return action_class178 except ImportError as e:179 raise ImportError(180 f"Failed to import action class for '{env_name}'.\n"181 f"Package '{env_info.package_name}' appears to be installed but the module cannot be imported.\n"182 f"Try reinstalling: pip install --force-reinstall {env_info.package_name}\n"183 f"Original error: {e}"184 ) from e185 186 @classmethod187 def from_hub(cls, env_name: str, skip_install: bool = False) -> Type:188 """189 Get the Action class from environment name.190 191 This is an alias for from_env() for backward compatibility and clarity.192 193 Args:194 env_name: Environment name (e.g., "coding", "echo")195 skip_install: If True, skip package installation and return196 GenericAction class instead.197 198 Returns:199 Action class (not an instance!)200 201 Examples:202 >>> CodeAction = AutoAction.from_hub("coding")203 >>> action = CodeAction(code="print('Hello!')")204 """205 return cls.from_env(env_name, skip_install=skip_install)206 207 @classmethod208 def get_action_info(cls, name: str) -> Dict[str, Any]:209 """210 Get detailed information about an action class.211 212 Args:213 name: Environment name214 215 Returns:216 Dictionary with action class metadata217 218 Raises:219 ValueError: If environment not found220 221 Examples:222 >>> info = AutoAction.get_action_info("coding")223 >>> print(info['action_class'])224 'CodingAction'225 >>> print(info['module'])226 'coding_env.client'227 """228 discovery = get_discovery()229 env_info = discovery.get_environment_by_name(name)230 231 if not env_info:232 raise ValueError(f"Unknown environment: {name}")233 234 return {235 "env_key": env_info.env_key,236 "env_name": env_info.name,237 "package": env_info.package_name,238 "action_class": env_info.action_class_name,239 "observation_class": env_info.observation_class_name,240 "module": env_info.client_module_path,241 }242 243 @classmethod244 def list_actions(cls) -> None:245 """246 Print a formatted list of all available action classes.247 248 This discovers all installed openenv-* packages and displays249 their action class information in a user-friendly format.250 251 Examples:252 >>> AutoAction.list_actions()253 Available Action Classes:254 ----------------------------------------------------------------------255 echo : EchoAction (from openenv-echo-env)256 coding : CodingAction (from openenv-coding_env)257 ----------------------------------------------------------------------258 Total: 2 action classes259 """260 discovery = get_discovery()261 environments = discovery.discover()262 263 print("Available Action Classes:")264 print("-" * 70)265 266 if not environments:267 print(" No OpenEnv environments found.")268 print(" Install environments with: pip install openenv-<env-name>")269 else:270 for env_key in sorted(environments.keys()):271 env = environments[env_key]272 print(f" {env_key:<15}: {env.action_class_name}")273 print(f" Package: {env.package_name}")274 275 print("-" * 70)276 print(f"Total: {len(environments)} action classes")277 