feat(workflow): add OpenEnvWorkflow adapter for HuggingFace OpenEnv - #1576
Draft
NancyFyong wants to merge 4 commits into
Draft
feat(workflow): add OpenEnvWorkflow adapter for HuggingFace OpenEnv#1576NancyFyong wants to merge 4 commits into
NancyFyong wants to merge 4 commits into
Conversation
Adds a generic RolloutWorkflow that drives any OpenEnv-compatible environment (BrowserGym, OpenSpiel, Coding, Chess, Echo, ...) via its uniform reset/step/state protocol. New environments plug in through YAML alone: users point at an EnvClient subclass, optionally an Action dataclass, and pick an action_parser / obs_formatter -- no new Python. Highlights: * areal/api/openenv_api.py: OpenEnvConfig + ActionParser/ObservationFormatter Protocols, with validation for base_url/provider mutex and provider extras. * areal/workflow/openenv.py: OpenEnvWorkflow.arun_episode drives one full episode, capturing per-step reward on each LLM turn via ArealOpenAI so GRPO grouping sees per-turn advantages. Supports terminal-reward-only, step-discount backward propagation, and optional reward_shaping_fn. * areal/workflow/openenv_utils.py: default JSON / tag / passthrough action parsers, Auto observation formatter that JSON-encodes dataclasses/dicts. * Non-Docker path: UVProvider (openenv >= 0.1) launches env projects via 'uv run', so this works on GPU clusters without a Docker daemon. * examples/openenv/: shared train.py + two configs -- echo_smoke (HF Space, zero local install) and blackjack_grpo (real GRPO curve via UVProvider). * docs/en/customization/openenv.md and README with 5-minute quickstart. * tests/test_openenv_workflow.py: mock-based unit tests covering config validation, parser variants, obs formatting, and the reset/step loop (done early-exit, max_turns cap, terminal_reward_only, discount hookup). Closes the "Support OpenEnv in Agentic RL" H2 2026 roadmap item (areal-project#1381). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ericEnvClient openenv 0.4 is the first release with the async EnvClient API (aenter/aexit, awaitable reset/step). 0.2 exposes only the sync surface, so our workflow's 'async with env_client as env' unwinds unusably against it. Also switch echo_smoke.yaml from echo_env.EchoEnv (requires installing the env-specific package) to openenv.core.generic_client.GenericEnvClient, which speaks the protocol by URL alone. This lets the smoke config run against https://openenv-echo-env.hf.space with zero extra installs. Verified end-to-end: * Layer 1: 22/22 unit tests pass (~35s) * Layer 2: OpenEnvWorkflow drives one episode against the public echo Space * Layer 3: 5 episodes against the public openspiel Space produce +/-1 rewards and honor done termination. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
NancyFyong
force-pushed
the
feat/openenv-adapter
branch
from
August 4, 2026 07:59
d6f8775 to
981f990
Compare
Round-1 review surfaced 4 HIGH and 4 MED items. Fixes bundled here. HIGH areal-project#1 -- parse-failure no longer poisons terminal_reward_only When a mid-trajectory action failed to parse, the workflow appended a 0.0 sentinel to step_rewards and completion_ids, then terminal_reward_only read step_rewards[-1] == 0.0 and zeroed every prior real turn. Now parse-failure sets the completion's reward directly but stays out of step_rewards/completion_ids, so the terminal bookkeeping still sees only real env interactions. Regression test added. HIGH areal-project#2/areal-project#3 -- uv.lock + pyproject.vllm.toml parity Regenerated uv.lock so `provides-extras` includes `openenv`; added the same `openenv = ["openenv>=0.4"]` block to pyproject.vllm.toml and regenerated uv.vllm.lock via the standard swap-lock-restore dance. HIGH areal-project#4 -- blackjack_grpo.yaml example was broken `_instantiate_env_client` imports `env_client_class` eagerly, so the yaml's `openspiel_env.OpenSpielEnv` reference required the package to be installed in the workflow process before UVProvider even ran -- and `openspiel_env` is not in the openenv extra. Switched the example to `openenv.core.generic_client.GenericEnvClient` (URL-only, zero extra install); kept `provider: uv` + `project_path` so UVProvider still launches the env server locally. MED areal-project#5 -- terminal_reward_only + step_discount interaction Back-propagating discount after zeroing intermediates smeared non-zero credit onto the 'discarded' turns, contradicting the docstring. Gated: `if terminal_reward_only ... elif step_discount < 1.0: apply_discount`. MED areal-project#6 -- per-episode seed now overrides workflow default Old logic (`if seed in data and seed not in reset_kwargs`) silently ignored per-row seeds when the config set a workflow-level default, giving identical env init across the whole batch. Now `data['seed']` always wins. MED areal-project#7 -- `provider: Literal["uv", "docker"] | None` for type-safe config. MED areal-project#8 -- docs/en/customization/openenv.md aligned with echo_smoke.yaml Docs example switched from `echo_env.EchoEnv` (needs separate install) to `openenv.core.generic_client.GenericEnvClient` (zero extra install), matching the reference YAML. README's 'add a new env' snippet updated the same way. Also drops `@runtime_checkable` from the Protocols (no isinstance() sites), emits `parse_failed` in the workflow stats, and adds 6 regression tests (28 total, up from 22). All ruff/format/tests clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…h import Adds a fourth deployment path to OpenEnvWorkflow that keeps everything local -- no HF Space upload, no Docker daemon, no HTTP server. The env class ships alongside its config and is loaded directly from disk. Changes: * `_import_from_string` now accepts `/abs/path/file.py:ClassName` in addition to dotted module paths. Uses `importlib.util.spec_from_file_location` and registers into `sys.modules` before `exec_module` so `@dataclass` / typing lookups via `cls.__module__` work correctly. Missing files raise ImportError (not silent success). Two regression tests cover both paths. * `examples/openenv/local_envs/blackjack_env.py` -- reference offline env (in-process BlackJack) implementing the OpenEnv async surface: __aenter__/__aexit__, reset(seed), step(action). * `examples/openenv/local_blackjack_grpo.yaml` -- ready-to-run GRPO config pointing at a local Qwen model + the in-process env. Zero external traffic at runtime. * `docs/en/customization/openenv.md` -- documents the 4 deployment modes (HF Space / UV / Docker / in-process) and flags the HF-Space data-leak concern for anyone doing production/sensitive training. Smoke verified: 30 random-policy episodes against LocalBlackjackEnv → 7/23 win/loss, matching a random-strategy baseline for BlackJack. 30/30 unit tests pass (up from 28); ruff check / format clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
This pull request has been automatically marked as stale because it has not had recent activity within the last 14 days. Please add a comment or push new commits to keep it active. Thank you for your contribution! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Add a generic
OpenEnvWorkflowthat speaks theHuggingFace OpenEnv protocol so any
OpenEnv-compatible environment (BrowserGym, OpenSpiel, coding sandboxes,
chess, echo, ...) can be trained against with GRPO through YAML alone -- no
new Python per environment.
Key pieces:
areal/api/openenv_api.py—OpenEnvConfigdataclass +ActionParser/ObservationFormatterProtocols, with post-init validation (base_urlxorprovider, provider extras).areal/workflow/openenv.py—OpenEnvWorkflow.arun_episodedrives thereset()→[chat.completion → parse action → env.step()] * Nloop. Per-step rewards go on each LLM completion viaArealOpenAI.set_reward, so GRPO grouping sees per-turn advantages. Supportsterminal_reward_only, backwardstep_discountpropagation, and an optionalreward_shaping_fn.areal/workflow/openenv_utils.py— defaultJSONActionParser/TagActionParser/PassthroughActionParser;AutoObservationFormatterthat JSON-encodes dataclasses / dicts / objects. Users can drop in custom parsers via dotted import path.UVProviderwhenprovider="uv", so the whole thing works on Slurm / K8s clusters without a Docker daemon.examples/openenv/— sharedtrain.py+echo_smoke.yaml(public HF Space, zero local install) +blackjack_grpo.yaml(UVProvider path).docs/en/customization/openenv.md— 5-minute guide covering install, parsers, formatters, reward propagation, and wiring in a new env.tests/test_openenv_workflow.py— 22 unit tests with mockedEnvClient/ArealOpenAI.Related Issue
Addresses the "Support OpenEnv in Agentic RL" H2 2026 roadmap item in #1381.
Type of Change
Checklist
pytest tests/test_openenv_workflow.py→ 22/22 pass in ~35sdocs/en/customization/openenv.md+ toc entry + example README)main/review-prcommand/create-prAdditional Context
Verification performed:
pytest tests/test_openenv_workflow.py(22 tests: config validation, parsers, formatter,arun_episodereset/step loop,doneearly-exit,max_turnscap,terminal_reward_only,step_discounthook)ruff check+ruff format --checkon all new fileshttps://openenv-echo-env.hf.space) — real WebSocket, real observation payloaddone, rewards ∈ {−1, +1}Not yet verified locally:
RolloutWorkflow+ArealOpenAIpath thatMultiTurnV2Workflowuses.Design notes:
openenv>=0.4(async EnvClient API landed in 0.4; 0.2.x is sync-only). Pinned as an optional extra.action_class), or a raw string; the parser layer decides.GenericEnvClientaccepts plain dicts and covers ~all built-in envs by URL alone.areal.workflow.__init__, so users who never touch it don't pay any import cost.How to try it locally: