From 7acaaf519aa6a16894e222aa8bf17f9e6b0241fe Mon Sep 17 00:00:00 2001 From: Beinan Wang Date: Mon, 13 Jul 2026 04:32:48 +0000 Subject: [PATCH] refactor(python): rename RemoteRolloutStore to AsyncRolloutStore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The class name described the wrong axis. `RemoteRolloutStore` wraps the exact same `_RolloutStore` as the sync `RolloutStore` — its only distinguishing feature is that it runs blocking calls in an executor so they can be awaited. "Remote" is also misleading because the sync `RolloutStore.connect()` already talks to a remote server; the async wrapper adds no remote-only behavior. `AsyncRolloutStore` names the real distinction (async vs sync) and aligns with the existing `AsyncContext` convention ("Async* = the async wrapper of a sync class"). Clean rename with no deprecated alias: the class shipped in 0.6.1 and has no callers beyond the remote test. --- python/python/lance_context/__init__.py | 4 ++-- python/python/lance_context/api.py | 16 +++++++++------- python/tests/test_rollout_remote.py | 10 +++++----- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/python/python/lance_context/__init__.py b/python/python/lance_context/__init__.py index b6c6f1b2..7854f0d0 100644 --- a/python/python/lance_context/__init__.py +++ b/python/python/lance_context/__init__.py @@ -2,11 +2,11 @@ from .api import ( # pyright: ignore[reportMissingImports] AsyncContext, + AsyncRolloutStore, Context, ContextNamespace, EmbeddingProvider, RemoteContext, - RemoteRolloutStore, RolloutStore, __version__, ) @@ -16,12 +16,12 @@ __all__ = [ "AsyncContext", + "AsyncRolloutStore", "Context", "ContextNamespace", "EmbeddingProvider", "MultiModalEmbeddingProvider", "RemoteContext", - "RemoteRolloutStore", "RolloutStore", "__version__", ] diff --git a/python/python/lance_context/api.py b/python/python/lance_context/api.py index d9fedebb..8c9425ed 100644 --- a/python/python/lance_context/api.py +++ b/python/python/lance_context/api.py @@ -26,11 +26,11 @@ __all__ = [ "AsyncContext", + "AsyncRolloutStore", "Context", "ContextNamespace", "EmbeddingProvider", "RemoteContext", - "RemoteRolloutStore", "RolloutStore", "__version__", ] @@ -2594,18 +2594,20 @@ def __repr__(self) -> str: return f"RolloutStore(version={self._sync.version()})" -class RemoteRolloutStore: - """Async wrapper around a remote rollout store over HTTP. +class AsyncRolloutStore: + """Async wrapper around a rollout store, for use from asyncio code. Mirrors :class:`RolloutStore` but runs the blocking calls in an executor so - they can be awaited. Every method matches its sync counterpart. + they can be awaited without blocking the event loop. Every method matches + its sync counterpart. This is the interface RL generation workers and the + learner use when talking to a remote ``lance-context-server`` over HTTP. """ def __init__(self, sync_store: _RolloutStore) -> None: self._sync = sync_store @classmethod - async def connect(cls, base_url: str, name: str) -> "RemoteRolloutStore": + async def connect(cls, base_url: str, name: str) -> "AsyncRolloutStore": """Connect to an existing remote rollout store.""" loop = asyncio.get_running_loop() sync_store = await loop.run_in_executor( @@ -2620,7 +2622,7 @@ async def connect_or_create( name: str, *, storage_options: Mapping[str, str] | None = None, - ) -> "RemoteRolloutStore": + ) -> "AsyncRolloutStore": """Connect to a remote rollout store, creating it if absent.""" opts = dict(storage_options) if storage_options else None loop = asyncio.get_running_loop() @@ -2675,4 +2677,4 @@ async def checkout(self, version: int) -> None: await loop.run_in_executor(None, lambda: self._sync.checkout(version)) def __repr__(self) -> str: - return f"RemoteRolloutStore(version={self._sync.version()})" + return f"AsyncRolloutStore(version={self._sync.version()})" diff --git a/python/tests/test_rollout_remote.py b/python/tests/test_rollout_remote.py index 2ae4b50b..00a342bd 100644 --- a/python/tests/test_rollout_remote.py +++ b/python/tests/test_rollout_remote.py @@ -1,7 +1,7 @@ """Remote (HTTP) rollout store tests. Spins up a real `lance-context-server` subprocess and drives it through the -async `RemoteRolloutStore` wrapper — the path RL generation workers and the +async `AsyncRolloutStore` wrapper — the path RL generation workers and the learner use in a deployed setup. Skips gracefully if the server binary has not been built (e.g. a pure-Python CI job). """ @@ -18,7 +18,7 @@ from pathlib import Path import pytest -from lance_context import RemoteRolloutStore +from lance_context import AsyncRolloutStore _REPO_ROOT = Path(__file__).resolve().parents[2] _SERVER_BIN = _REPO_ROOT / "target" / "debug" / "lance-context-server" @@ -76,7 +76,7 @@ def server(): def test_remote_roundtrip(server): async def run(): - store = await RemoteRolloutStore.connect_or_create(server, "rl-run-1") + store = await AsyncRolloutStore.connect_or_create(server, "rl-run-1") resp = await store.add( [ @@ -119,12 +119,12 @@ async def run(): def test_remote_add_one_and_connect(server): async def run(): - store = await RemoteRolloutStore.connect_or_create(server, "rl-run-2") + store = await AsyncRolloutStore.connect_or_create(server, "rl-run-2") await store.add_one(id="only", rollout_id="traj-9", reward=0.5) # A second connection sees the first's flushed write (durable, no # read affinity). - reader = await RemoteRolloutStore.connect(server, "rl-run-2") + reader = await AsyncRolloutStore.connect(server, "rl-run-2") rows = await reader.list() assert [r["id"] for r in rows] == ["only"]