Skip to content

core: make EnvClient WebSocket keepalive (ping_interval) configurable #875

Description

@sergiopaniego

Problem

EnvClient opens its WebSocket with the websockets library defaults, which send keepalive pings (~every 20s) and expect pongs:

https://github.com/huggingface/OpenEnv/blob/main/src/openenv/core/env_client.py#L214

self._ws = await ws_connect(
    self._ws_url,
    open_timeout=self._connect_timeout,
    max_size=self._max_message_size,
    **connect_kwargs,
)

Some cloud sandbox tunnels do not forward WebSocket ping/pong frames. Modal's encrypted tunnel is one: with the default ping_interval, the client never receives a pong and closes the connection on the keepalive timeout. This surfaced in #821 (ModalProvider), where the only working E2E required monkeypatching:

import openenv.core.env_client as _ec
_ec.ws_connect = functools.partial(_ec.ws_connect, ping_interval=None)

This is a core limitation, not a provider bug: the provider returns a valid https/wss base_url and has no hook into how EnvClient opens its socket. It also conflicts with the cloud-provider contract in RFC 002 (the returned base_url should work with a stock EnvClient).

Proposed change

Make the WebSocket keepalive configurable on EnvClient (e.g. ping_interval / ping_timeout constructor args, forwarded to ws_connect), so sandbox services whose tunnels drop ping/pong work without monkeypatching. Open questions:

  • Sensible default: keep the current keepalive, or relax it for robustness across tunnels?
  • Whether a ContainerProvider should be able to hint a value (e.g. Modal returning ping_interval=None) so users do not have to know the tunnel quirk.

Context

  • Raised in Add ModalProvider(ContainerProvider) #821 (review) by @burtenshaw: "Make Modal work through stock EnvClient ... lmk if we should make changes in core to facilitate sandbox services without the ws ping route."
  • Unblocks merging Modal and other tunnel-backed providers without the client-side workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions