Skip to content

OpenEnv clients async support update#4949

Open
sergiopaniego wants to merge 10 commits intomainfrom
async-openenv
Open

OpenEnv clients async support update#4949
sergiopaniego wants to merge 10 commits intomainfrom
async-openenv

Conversation

@sergiopaniego
Copy link
Member

@sergiopaniego sergiopaniego commented Feb 2, 2026

What does this PR do?

Minimal changes to support the changes in OpenEnv for making the EnvClient async by default (meta-pytorch/OpenEnv#343).

Before submitting

  • This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case).
  • Did you read the contributor guideline,
    Pull Request section?
  • Was this discussed/approved via a GitHub issue? Please add a link
    to it if that's the case.
  • Did you make sure to update the documentation with your changes?
  • Did you write any new necessary tests?

Who can review?

@burtenshaw @kashif @albertvillanova @qgallouedec

@HuggingFaceDocBuilderDev

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

# Handle async rollout_func
output = asyncio.run(rollout_func(rollout_prompts))
else:
# [Legacy] Handle sync rollout_func
Copy link
Member

Choose a reason for hiding this comment

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

should we consider this as legacy?

Copy link
Member Author

Choose a reason for hiding this comment

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

For OpenEnv, the new default is to use async so it'll be only useful for some old envs but I think having there "Legacy" may be misleading from TRL-side. I've removed it.

@sergiopaniego
Copy link
Member Author

Changes extended to notebooks.
Changes are already needed since we install OpenEnv using the environments via pip and looking at the dependencies management, it installs OpenEnv from main (example)

Copy link
Member

@albertvillanova albertvillanova left a comment

Choose a reason for hiding this comment

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

Thanks for addressing this important issue!

Some comments/questions below.

vllm_mode=args.vllm_mode,
vllm_server_base_url=args.vllm_server_url if args.vllm_mode == "server" else None,
vllm_gpu_memory_utilization=0.4,
vllm_gpu_memory_utilization=0.5,
Copy link
Member

Choose a reason for hiding this comment

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

Is this change intended? Why?

Copy link
Member Author

Choose a reason for hiding this comment

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

reverted!

# Support both sync and async rollout functions:
if inspect.iscoroutinefunction(rollout_func):
# Handle async rollout_func
output = asyncio.run(rollout_func(rollout_prompts))
Copy link
Member

Choose a reason for hiding this comment

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

This may raise an error if run in an environment with an active event loop (notebooks, IPython,...).

Copy link
Member Author

Choose a reason for hiding this comment

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

great catch! I've handled it adding a nest_asyncio. Tested and working!

Copy link
Member

Choose a reason for hiding this comment

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

Thanks again for addressing this and your proposed solution.

However, I am still concerned about this approach:

  • Proposing to use nest_asyncio in our example notebooks, but keeping the call to asyncio.run in the codebase

Some of my concerns (just brainstorming; please, feel free to give your opinions/arguments on this):

  • in my opinion, the lib codebase itself should support handling a running loop, so async rollouts are reliable in common environments
  • in my opinion, the use of nest_asyncio is a "reasonable" notebook workaround, but not a robust base‑code fix
    • it patches the running event loop to allow re‑entrant calls like asyncio.run inside an active loop
    • this can avoid the immediate RuntimeError and may appear to work in notebooks
    • but it's still blocking the loop thread and can lead to subtle hangs if the coroutine depends on other tasks that need the loop to make progress (custom rollout_func on same loop, tool-calling or streaming generation, IPython kernel, UI callbacks, background progress, etc.)
  • on the other hand, we should be aware that it is not straightforward to support handling a running loop:
    • our use case: awaiting async code in a synchronous function running inside an active event loop on the same thread
    • problem: blocking on async results in the same thread causes deadlock
    • possible solution: run async code in a new background thread when a loop is running; otherwise use asyncio.run directly
  • in this line, I see that OpenEnv codebase implemented a run_async_safely function: https://github.com/meta-pytorch/OpenEnv/pull/343/changes#diff-897378bca76b3c096bfbfa9da0e18034a0ead03bd8a27f976e0fbc990a4360c2R13
    • I think that approach goes in the right direction
    • but not 100% sure about their specific implementation:
      • I think it still blocks the current thread while waiting for future.result()
      • I could open an issue on their repo and discuss about this

As I said above, just brainstorming. Feel free to disagree. Also, we could address these subtleties in a subsequent PR if you prefer.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks a lot for all the details! I've tried generating a common solution for all the different cases (script/notebook). Both are now managed from TRL-side without introducing changes in the notebook. Since Colab already comes with nest_asyncio installed, we don't need to install the dependency. What do you think?

]
output = rollout_func(rollout_prompts)
# Support both sync and async rollout functions:
if inspect.iscoroutinefunction(rollout_func):
Copy link
Member

@albertvillanova albertvillanova Feb 3, 2026

Choose a reason for hiding this comment

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

Whereas this check is enough for native coroutine functions (defined with async def), it will fail for async callables and awaitables: partials, callables with __call__, decorated functions, functions that return coroutines...

Do you think we should support all those edge cases or it is enough for the moment to check just for coroutines?

Copy link
Member Author

Choose a reason for hiding this comment

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

mmm I think it's ok to just add the current support

Copy link
Member

Choose a reason for hiding this comment

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

Maybe a naive question: is async rollout_func intended to be supported in both server and colocate modes?

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks for this one! It was missing 😄 added

@sergiopaniego
Copy link
Member Author

updated @albertvillanova

btw, maybe we can now wait for a new OpenEnv version before merging this one, since we're now pinging the version both in TRL and in OpenEnv (remote HF Spaces included) and that version doesn't include the async client

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants