From 5b7264752d557f2c3b9e7abded918d79382fa9c1 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 18:39:05 +0000 Subject: [PATCH 1/7] pin prime-runs and prime-traces to concurrent uploads PrimeIntellect-ai/prime#1136 gzips at level 1, keeps up to eight upload requests in flight, and bounds the upload queue by records with a submit that never blocks. prime-runs moves into override-dependencies next to prime-traces, because verifiers pins it to an older commit. Co-Authored-By: Claude Opus 5.5 --- pyproject.toml | 10 ++++++---- uv.lock | 51 +++++++++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 34f23c524b3..e709ca7c6f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,8 +158,10 @@ override-dependencies = [ "nvidia-cutlass-dsl==4.8.0", "torch>=2.13.0", "openenv-core", - # TODO: PyPI release of prime-traces with PrimeIntellect-ai/prime#1109 (orjson records) asap. - "prime-traces @ git+https://github.com/PrimeIntellect-ai/prime@adf8d25c3e34df8e039674fae9e0e4bf9cee27f3#subdirectory=packages/prime-traces", + # TODO: PyPI release of prime-runs and prime-traces with PrimeIntellect-ai/prime#1136 (non-blocking, + # concurrent uploads) asap. Overridden here because verifiers pins prime-runs to an older commit. + "prime-runs[train] @ git+https://github.com/PrimeIntellect-ai/prime@d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#subdirectory=packages/prime-runs", + "prime-traces @ git+https://github.com/PrimeIntellect-ai/prime@d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#subdirectory=packages/prime-traces", ] # prime-envs packages are auto-discovered as workspace members, so new @@ -266,9 +268,9 @@ prime-rl-configs = { path = "packages/prime-rl-configs", editable = true } verifiers = { path = "deps/verifiers", editable = true } renderers = { path = "deps/renderers", editable = true } prime-pydantic-config = { path = "deps/pydantic-config", editable = true } -# TODO: replace with the prime-runs PyPI release that has PrimeIntellect-ai/prime#1109 asap, and raise the +# TODO: replace with the prime-runs PyPI release that has PrimeIntellect-ai/prime#1136 asap, and raise the # prime-runs minimum to it (prime-traces is pinned in override-dependencies until then). -prime-runs = { git = "https://github.com/PrimeIntellect-ai/prime", subdirectory = "packages/prime-runs", rev = "adf8d25c3e34df8e039674fae9e0e4bf9cee27f3" } +prime-runs = { git = "https://github.com/PrimeIntellect-ai/prime", subdirectory = "packages/prime-runs", rev = "d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" } torch = [{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" }] torchvision = [{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" }] torchaudio = [{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" }] diff --git a/uv.lock b/uv.lock index 69b2a5b4a74..ce70417eac7 100644 --- a/uv.lock +++ b/uv.lock @@ -17,33 +17,33 @@ exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for exclude-newer-span = "P7D" [options.exclude-newer-package] -certifi = false -deep-ep = false -deep-gemm = false -deep-select = false +verifiers = false +prime-pydantic-config = false +harbor = "2026-10-08T00:00:00Z" +prime-traces = false +vllm-router = false dion = false -fastokens = false +deep-select = false +prime-runs = false flash-attn-3 = false +prime-tunnel = false flash-mla = false flashinfer-python = false -harbor = "2026-10-08T00:00:00Z" humming-kernels = false +prime-evals = false +deep-gemm = false +torchao = false instanttensor = false -modelexpress = false -nixl-cu13 = false prime = false -prime-evals = false -prime-kernels = false -prime-pydantic-config = false -prime-runs = false +tokenspeed-mla = false +nixl-cu13 = false +deep-ep = false +certifi = false prime-sandboxes = false -prime-traces = false -prime-tunnel = false renderers = false -tokenspeed-mla = false -torchao = false -verifiers = false -vllm-router = false +modelexpress = false +prime-kernels = false +fastokens = false [manifest] members = [ @@ -164,7 +164,8 @@ overrides = [ { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'", specifier = ">=9.15" }, { name = "nvidia-cutlass-dsl", specifier = "==4.8.0" }, { name = "openenv-core" }, - { name = "prime-traces", git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-traces&rev=adf8d25c3e34df8e039674fae9e0e4bf9cee27f3" }, + { name = "prime-runs", extras = ["train"], git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" }, + { name = "prime-traces", git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-traces&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" }, { name = "tilelang", specifier = "==0.1.12" }, { name = "torch", marker = "sys_platform != 'linux'", specifier = ">=2.13.0" }, { name = "torch", marker = "sys_platform == 'linux'", specifier = ">=2.13.0", index = "https://download.pytorch.org/whl/cu130" }, @@ -5408,7 +5409,7 @@ requires-dist = [ { name = "prime-rl", extras = ["quack"], marker = "extra == 'all'" }, { name = "prime-rl", extras = ["sonic-moe"], marker = "extra == 'all'" }, { name = "prime-rl-configs", editable = "packages/prime-rl-configs" }, - { name = "prime-runs", extras = ["train"], git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=adf8d25c3e34df8e039674fae9e0e4bf9cee27f3" }, + { name = "prime-runs", extras = ["train"], git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" }, { name = "prometheus-client", specifier = ">=0.20.0" }, { name = "psutil", specifier = ">=7.0.0" }, { name = "pybase64", specifier = ">=1.4.2" }, @@ -5482,8 +5483,8 @@ requires-dist = [ [[package]] name = "prime-runs" -version = "0.1.4" -source = { git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=adf8d25c3e34df8e039674fae9e0e4bf9cee27f3#adf8d25c3e34df8e039674fae9e0e4bf9cee27f3" } +version = "0.1.5" +source = { git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" } dependencies = [ { name = "httpx" }, { name = "orjson" }, @@ -5516,8 +5517,8 @@ wheels = [ [[package]] name = "prime-traces" -version = "0.0.10" -source = { git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-traces&rev=adf8d25c3e34df8e039674fae9e0e4bf9cee27f3#adf8d25c3e34df8e039674fae9e0e4bf9cee27f3" } +version = "0.0.11" +source = { git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-traces&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" } dependencies = [ { name = "httpx" }, { name = "orjson" }, @@ -7992,7 +7993,7 @@ dependencies = [ { name = "openai" }, { name = "orjson" }, { name = "prime-pydantic-config", extra = ["toml"] }, - { name = "prime-runs" }, + { name = "prime-runs", extra = ["train"] }, { name = "prime-sandboxes" }, { name = "prime-tunnel" }, { name = "pydantic" }, From 09d60c7e210fe3507e2be986dd95032e463756c4 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 18:39:05 +0000 Subject: [PATCH 2/7] log to prime without a thread hop The SDK's submit never blocks now, so the prime monitor calls run.log_metrics and run.log_episodes on the event loop. Each asyncio.to_thread hop had to win the GIL from the uploader thread several times, so under load a log() call took up to 87 ms. The load test drops its expected failure for 1,000 episodes. Its fake Traces endpoint gzips at the SDK's level and counts lines under a lock, since the client now sends several requests at once. Co-Authored-By: Claude Opus 5.5 --- src/prime_rl/monitors/prime.py | 12 ++++++------ tests/unit/monitors/test_monitor_load.py | 24 +++++++++--------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/prime_rl/monitors/prime.py b/src/prime_rl/monitors/prime.py index 4f4b8f5ddaf..a708eb3b705 100644 --- a/src/prime_rl/monitors/prime.py +++ b/src/prime_rl/monitors/prime.py @@ -199,9 +199,9 @@ async def log_metrics(self, metrics: dict[str, Any], step: int | None) -> None: metrics, dropped = sanitize(metrics) if dropped: self.logger.warning(f"Dropping {len(dropped)} non-finite metric value(s): {', '.join(dropped[:5])}") - # A queue put that can block briefly under backpressure - off the loop. The SDK - # stamps `_timestamp` on every row, so step=None rows keep a time anchor. - await asyncio.to_thread(self.run.log_metrics, metrics, step=step) + # A queue put that never blocks. The SDK stamps `_timestamp` on every row, so + # step=None rows keep a time anchor. + self.run.log_metrics(metrics, step=step) async def log_episodes(self, episodes: list[vf.Episode], step: int, kind: Kind, subset: Subset) -> None: """Every episode ships whole as it arrives (the ``all`` subset), errored and @@ -211,8 +211,8 @@ async def log_episodes(self, episodes: list[vf.Episode], step: int, kind: Kind, if subset != "all" or not episodes: return if self.run is not None: - # A queue put that can block briefly under backpressure - off the loop. - await asyncio.to_thread(self.run.log_episodes, episodes) + # A queue put that never blocks. + self.run.log_episodes(episodes) return by_env: dict[str, list[vf.Episode]] = {} for episode in episodes: @@ -221,7 +221,7 @@ async def log_episodes(self, episodes: list[vf.Episode], step: int, kind: Kind, for env_name, batch in by_env.items(): run = await self.run_for(env_name, step) if run is not None: - await asyncio.to_thread(run.log_episodes, batch) # a queue put, off the loop + run.log_episodes(batch) async def finalize(self, paused: bool = False) -> None: # An epoch the run did not finish leaves its evaluation open: close it as cancelled. diff --git a/tests/unit/monitors/test_monitor_load.py b/tests/unit/monitors/test_monitor_load.py index 0f64fd061a6..37ff9f466e0 100644 --- a/tests/unit/monitors/test_monitor_load.py +++ b/tests/unit/monitors/test_monitor_load.py @@ -8,6 +8,7 @@ import gc import gzip import statistics +import threading import time import numpy as np @@ -18,6 +19,7 @@ from prime_runs.sinks import RftMetricsSink, TracesSink from prime_runs.worker import UploadWorker from prime_traces import TracesClient +from prime_traces.core.client import GZIP_LEVEL from verifiers.v1.graph import MessageNode from verifiers.v1.trace import ModelCall from verifiers.v1.types import AssistantMessage, Usage, UserMessage @@ -175,15 +177,18 @@ def registered(): class FakeTracesAPI: """The Prime Traces endpoint: gzips each request like the real client and keeps - the lines it received.""" + the lines it received. The client sends several requests at once.""" def __init__(self) -> None: self.lines = 0 + self._lock = threading.Lock() def upload_batch(self, body, idempotency_key, *, line_format, schema_version=1, context=None, compress=True): if compress: - gzip.compress(body, mtime=0) - self.lines += body.count(b"\n") + (not body.endswith(b"\n")) + gzip.compress(body, GZIP_LEVEL, mtime=0) + lines = body.count(b"\n") + (not body.endswith(b"\n")) + with self._lock: + self.lines += lines return {"upload_id": idempotency_key.split(":", 1)[1], "status": "committed"} def close(self) -> None: @@ -257,21 +262,10 @@ def counting_log(data, *args, **kwargs): return monitor, lambda: (None, steps) -# TODO: fix and drop this mark. The SDK uploads one episode at a time, and gzips each -# at level 9 (about 70 ms here). A thousand episodes logged at once fill its queue, and -# log() then waits for room (up to 5 s) on the orchestrator's arrival path, then drops. -SDK_BACKPRESSURE = pytest.mark.xfail(reason="prime_runs blocks log() while its upload queue is full", strict=True) - - @pytest.mark.parametrize( "start,count", [ - pytest.param( - start, - count, - id=f"{start.__name__[1:]}-{count}", - marks=SDK_BACKPRESSURE if (start, count) == (_prime, 1000) else (), - ) + pytest.param(start, count, id=f"{start.__name__[1:]}-{count}") for start in (_file, _prime, _wandb) for count in (10, 100, 1000) ], From 39320f93dcf179cf72a552816f3b80f319402b49 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 19:17:46 +0000 Subject: [PATCH 3/7] size the load test's episodes like a production run The load test used one 2.2 MB episode. A production RL run's episodes are larger and vary a lot: median 6.4 MB, p90 17 MB, p99 35 MB, uncompressed. Each episode now draws its size from a lognormal through that median and p90, which also matches the run's mean and p99. Sizes are capped at 60 MB, under Prime Traces' 64 MiB line limit. The episodes share 24 traces at log-spaced sizes, so the test's memory stays small. A thousand episodes is now about 8 GB of JSON, close to one production step of 1,024 samples. The file monitor's check counted only the first trace chunk, read whole. It now streams every chunk, since 8 GB spans two. Co-Authored-By: Claude Opus 5.5 --- tests/unit/monitors/test_monitor_load.py | 75 +++++++++++++++--------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/tests/unit/monitors/test_monitor_load.py b/tests/unit/monitors/test_monitor_load.py index 37ff9f466e0..a303d1d9273 100644 --- a/tests/unit/monitors/test_monitor_load.py +++ b/tests/unit/monitors/test_monitor_load.py @@ -1,5 +1,5 @@ -"""Each monitor under an orchestrator-sized load: up to a thousand agentic episodes -(100 turns, 100k tokens each) and thousands of metric rows, logged through +"""Each monitor under an orchestrator-sized load: up to a thousand agentic episodes, +sized like a production RL run's, and thousands of metric rows, logged through ``monitors.log`` as the orchestrator does. Everything logged must arrive, and the event loop must stay responsive while the monitor writes or uploads.""" @@ -28,14 +28,23 @@ from prime_rl.configs.monitors import FileMonitorConfig, PrimeMonitorConfig, WandbMonitorConfig from prime_rl.monitors.file.monitor import FileMonitor from prime_rl.monitors.file.traces import get_trace_stream -from prime_rl.monitors.file.traces.chunks import open_chunk +from prime_rl.monitors.file.traces.chunks import chunk_numbers, open_chunk from prime_rl.monitors.prime import PrimeMonitor from prime_rl.monitors.wandb import WandbMonitor from prime_rl.orchestrator.gc import GarbageCollector from prime_rl.utils.pathing import get_file_monitor_dir -TURNS = 100 TOKENS_PER_MESSAGE = 500 +# Episode sizes (uncompressed JSON) in a production RL run: median 6.4 MB, p90 17 MB, +# p99 35 MB. A lognormal through the median and p90 also matches its mean (8.5 MB) +# and p99. +SIZE_MEDIAN_MB = 6.4 +SIZE_SIGMA = 0.76 +# Prime Traces rejects an episode over 64 MiB. +MAX_SIZE_MB = 60 +# Episodes share traces at these sizes, which keeps the test's memory small; each +# episode takes the one nearest its drawn size. +TRACE_SIZES_MB = np.geomspace(0.5, MAX_SIZE_MB, 24) METRIC_ROWS = 2_000 METRIC_KEYS = 200 # The orchestrator's own work runs between monitor calls; a stall this long delays @@ -47,11 +56,11 @@ pytestmark = pytest.mark.slow -def _trace(rng: np.random.Generator) -> vf.Trace: +def _trace(rng: np.random.Generator, turns: int) -> vf.Trace: """A multi-turn trace with per-token streams as the env-server wire delivers them.""" n = TOKENS_PER_MESSAGE nodes = [] - for _ in range(TURNS): + for _ in range(turns): nodes.append( MessageNode( parent=len(nodes) - 1 if nodes else None, @@ -81,30 +90,36 @@ def _trace(rng: np.random.Generator) -> vf.Trace: rewards={"correct": vf.Reward(score=1.0)}, calls=[ ModelCall(node=2 * i + 1, usage=Usage(prompt_tokens=n, completion_tokens=n), finish_reason="stop") - for i in range(TURNS) + for i in range(turns) ], ) +def _episode(trace: vf.Trace, i: int) -> vf.Episode: + return vf.Episode( + env=vf.EnvInfo(id="load", name="load"), + task=trace.task, + group=vf.GroupInfo(id=f"g{i // 8}"), + traces=[trace], + ok=True, + run=vf.TrainRunInfo(id="run", work=vf.TrainWorkInfo(step=1, policy=vf.PolicySpan(start=0, end=0))), + ) + + @pytest.fixture(scope="module") -def trace() -> vf.Trace: - return _trace(np.random.default_rng(0)) - - -def _episodes(trace: vf.Trace, count: int) -> list[vf.Episode]: - """``count`` episodes that share one trace: every monitor serializes each episode - on its own, so the load is the same as distinct traces at a fraction of the memory.""" - return [ - vf.Episode( - env=vf.EnvInfo(id="load", name="load"), - task=trace.task, - group=vf.GroupInfo(id=f"g{i // 8}"), - traces=[trace], - ok=True, - run=vf.TrainRunInfo(id="run", work=vf.TrainWorkInfo(step=1, policy=vf.PolicySpan(start=0, end=0))), - ) - for i in range(count) - ] +def traces() -> list[vf.Trace]: + """One trace per size in ``TRACE_SIZES_MB``.""" + rng = np.random.default_rng(0) + turn_mb = len(_episode(_trace(rng, 10), 0).to_json()) / 10 / 1e6 + return [_trace(rng, max(1, round(size / turn_mb))) for size in TRACE_SIZES_MB] + + +def _episodes(traces: list[vf.Trace], count: int) -> list[vf.Episode]: + """``count`` episodes with production sizes. Every monitor serializes each episode on + its own, so sharing traces keeps the load of distinct ones at a fraction of the memory.""" + sizes = np.random.default_rng(1).lognormal(np.log(SIZE_MEDIAN_MB), SIZE_SIGMA, count) + nearest = np.abs(np.log(sizes)[:, None] - np.log(TRACE_SIZES_MB)).argmin(1) + return [_episode(traces[index], i) for i, index in enumerate(nearest)] def _metrics(step: int) -> dict[str, float]: @@ -212,8 +227,10 @@ async def _file(tmp_path, monkeypatch): await monitor.init(tmp_path) def delivered() -> tuple[int | None, list[int]]: - with open_chunk(get_trace_stream(tmp_path), 0) as chunk: - episodes = sum(1 for _ in chunk.read().splitlines()) + stream, episodes = get_trace_stream(tmp_path), 0 + for number in chunk_numbers(stream): + with open_chunk(stream, number) as chunk: + episodes += sum(1 for _ in chunk) rows = (get_file_monitor_dir(tmp_path) / "metrics.jsonl").read_bytes().splitlines() return episodes, [orjson.loads(row)["step"] for row in rows] @@ -270,8 +287,8 @@ def counting_log(data, *args, **kwargs): for count in (10, 100, 1000) ], ) -def test_monitor_under_load(start, count, trace, tmp_path, registered, orchestrator_gc, monkeypatch): - episodes = _episodes(trace, count) +def test_monitor_under_load(start, count, traces, tmp_path, registered, orchestrator_gc, monkeypatch): + episodes = _episodes(traces, count) async def run(): monitor, delivered = await start(tmp_path, monkeypatch) From bd65fafccce682ce0b2c1da15a37cdc24dff59a7 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 20:45:04 +0000 Subject: [PATCH 4/7] give log() calls their own limit in the load test A log() call may await the monitor's own work: the file monitor awaits its write on a worker thread, which encodes the episode. At production sizes that takes up to ~400 ms on CI for a 60 MB episode, while the event loop keeps serving other tasks. The lag limits cover the loop, so log() calls get a looser limit of 1 s. Co-Authored-By: Claude Opus 5.5 --- tests/unit/monitors/test_monitor_load.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/monitors/test_monitor_load.py b/tests/unit/monitors/test_monitor_load.py index a303d1d9273..e7f05b8a723 100644 --- a/tests/unit/monitors/test_monitor_load.py +++ b/tests/unit/monitors/test_monitor_load.py @@ -51,6 +51,9 @@ # dispatch and batch assembly visibly. MAX_LAG_S = 0.25 P99_LAG_S = 0.05 +# A log() call may await the monitor's own work, such as the file monitor's write on its +# writer thread; the loop keeps serving other tasks meanwhile, which the lag limits cover. +MAX_LOG_S = 1.0 TICK_S = 0.005 pytestmark = pytest.mark.slow @@ -169,7 +172,7 @@ def _assert_responsive(name: str, lags: list[float], calls: list[float]) -> None ) assert p99 < P99_LAG_S, f"{name} stalls the event loop: p99 lag {p99 * 1e3:.0f} ms" assert max(lags) < MAX_LAG_S, f"{name} stalls the event loop: max lag {max(lags) * 1e3:.0f} ms" - assert max(calls) < MAX_LAG_S, f"a {name} log() call took {max(calls) * 1e3:.0f} ms" + assert max(calls) < MAX_LOG_S, f"a {name} log() call took {max(calls) * 1e3:.0f} ms" @pytest.fixture From 9b9279e9f51f5fe1f0395078a3c38e83cdfa36b9 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 20:56:18 +0000 Subject: [PATCH 5/7] raise the load test's p99 lag limit to 100 ms At production sizes the file monitor encodes each episode in one call, which stalls the event loop with a p99 of ~60 ms on CI. The limit covers that until the encoder works in short steps, which lowers it. Co-Authored-By: Claude Opus 5.5 --- tests/unit/monitors/test_monitor_load.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/monitors/test_monitor_load.py b/tests/unit/monitors/test_monitor_load.py index e7f05b8a723..9c91b1fc3fc 100644 --- a/tests/unit/monitors/test_monitor_load.py +++ b/tests/unit/monitors/test_monitor_load.py @@ -50,7 +50,7 @@ # The orchestrator's own work runs between monitor calls; a stall this long delays # dispatch and batch assembly visibly. MAX_LAG_S = 0.25 -P99_LAG_S = 0.05 +P99_LAG_S = 0.1 # A log() call may await the monitor's own work, such as the file monitor's write on its # writer thread; the loop keeps serving other tasks meanwhile, which the lag limits cover. MAX_LOG_S = 1.0 From 85bbed6206c7f09e500b4e542c4ecb9ff5adf5a5 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 23:51:47 +0000 Subject: [PATCH 6/7] pin prime-runs 0.1.6 and prime-traces 0.0.12 Co-Authored-By: Claude Opus 5.5 --- pyproject.toml | 12 ++++-------- uv.lock | 22 +++++++++++++++------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e709ca7c6f6..4e8e10153aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ dependencies = [ "wandb>=0.26.1", "wandb-workspaces>=0.4.3", "prime>=0.18.0", - "prime-runs[train]>=0.1.4", + "prime-runs[train]>=0.1.6", "pyzmq>=27.1.0", "aiolimiter>=1.2.1", "tenacity>=8.2.0", @@ -158,10 +158,9 @@ override-dependencies = [ "nvidia-cutlass-dsl==4.8.0", "torch>=2.13.0", "openenv-core", - # TODO: PyPI release of prime-runs and prime-traces with PrimeIntellect-ai/prime#1136 (non-blocking, - # concurrent uploads) asap. Overridden here because verifiers pins prime-runs to an older commit. - "prime-runs[train] @ git+https://github.com/PrimeIntellect-ai/prime@d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#subdirectory=packages/prime-runs", - "prime-traces @ git+https://github.com/PrimeIntellect-ai/prime@d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#subdirectory=packages/prime-traces", + # Overridden because the verifiers submodule pins prime-runs and prime-traces to an older commit. + "prime-runs[train]>=0.1.6", + "prime-traces>=0.0.12", ] # prime-envs packages are auto-discovered as workspace members, so new @@ -268,9 +267,6 @@ prime-rl-configs = { path = "packages/prime-rl-configs", editable = true } verifiers = { path = "deps/verifiers", editable = true } renderers = { path = "deps/renderers", editable = true } prime-pydantic-config = { path = "deps/pydantic-config", editable = true } -# TODO: replace with the prime-runs PyPI release that has PrimeIntellect-ai/prime#1136 asap, and raise the -# prime-runs minimum to it (prime-traces is pinned in override-dependencies until then). -prime-runs = { git = "https://github.com/PrimeIntellect-ai/prime", subdirectory = "packages/prime-runs", rev = "d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" } torch = [{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" }] torchvision = [{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" }] torchaudio = [{ index = "pytorch-cu130", marker = "sys_platform == 'linux'" }] diff --git a/uv.lock b/uv.lock index ce70417eac7..0dd54f024ca 100644 --- a/uv.lock +++ b/uv.lock @@ -164,8 +164,8 @@ overrides = [ { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'", specifier = ">=9.15" }, { name = "nvidia-cutlass-dsl", specifier = "==4.8.0" }, { name = "openenv-core" }, - { name = "prime-runs", extras = ["train"], git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" }, - { name = "prime-traces", git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-traces&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" }, + { name = "prime-runs", extras = ["train"], specifier = ">=0.1.6" }, + { name = "prime-traces", specifier = ">=0.0.12" }, { name = "tilelang", specifier = "==0.1.12" }, { name = "torch", marker = "sys_platform != 'linux'", specifier = ">=2.13.0" }, { name = "torch", marker = "sys_platform == 'linux'", specifier = ">=2.13.0", index = "https://download.pytorch.org/whl/cu130" }, @@ -5409,7 +5409,7 @@ requires-dist = [ { name = "prime-rl", extras = ["quack"], marker = "extra == 'all'" }, { name = "prime-rl", extras = ["sonic-moe"], marker = "extra == 'all'" }, { name = "prime-rl-configs", editable = "packages/prime-rl-configs" }, - { name = "prime-runs", extras = ["train"], git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" }, + { name = "prime-runs", extras = ["train"], specifier = ">=0.1.6" }, { name = "prometheus-client", specifier = ">=0.20.0" }, { name = "psutil", specifier = ">=7.0.0" }, { name = "pybase64", specifier = ">=1.4.2" }, @@ -5483,13 +5483,17 @@ requires-dist = [ [[package]] name = "prime-runs" -version = "0.1.5" -source = { git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" } +version = "0.1.6" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "orjson" }, { name = "prime-traces" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/44/76/a6810b874ed23b16f5210feb50c034595bd199a21a6db2619eef968c487b/prime_runs-0.1.6.tar.gz", hash = "sha256:60c2c656c9c076bd8995a3943b060fdaa893a248f9f627d5f2b26040821e829e", size = 68575, upload-time = "2026-11-02T23:44:48.27Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/22/8745496681a8ec9884f049acaf848642adea0fb529a817d55a1d05a4eebf/prime_runs-0.1.6-py3-none-any.whl", hash = "sha256:78934a05fa80c0abf3ccaee4b37bfe89c822a72b10231f9e667ce37586a1cbf6", size = 46844, upload-time = "2026-11-02T23:44:46.867Z" }, +] [package.optional-dependencies] train = [ @@ -5517,13 +5521,17 @@ wheels = [ [[package]] name = "prime-traces" -version = "0.0.11" -source = { git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-traces&rev=d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c#d6ab9cefaa47ef56bb1945d6a99cb690c67d0a7c" } +version = "0.0.12" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "orjson" }, { name = "pydantic" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/69/50/de56a11d243fddb0b7c9feb47e512681d5a1a499ef7c42ba33bbb34e3f86/prime_traces-0.0.12.tar.gz", hash = "sha256:bc34ab96fd7bb9bed3ab39f28546fa936bdebf2e900523c8aee2a4be6ae3ea37", size = 61727, upload-time = "2026-11-02T23:44:51.943Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/35/19558b051a460662db16e630492d6aa70ce714afa2421c3d11f4883b6f01/prime_traces-0.0.12-py3-none-any.whl", hash = "sha256:9f6bba4fb0f6fcf09662475bdf86e81f42df94cce27409ca7acba3b7d1e90025", size = 44478, upload-time = "2026-11-02T23:44:50.652Z" }, +] [[package]] name = "prime-tunnel" From 1d3e98a6f21793c8b31235077a2e513512f72ca2 Mon Sep 17 00:00:00 2001 From: Mika Senghaas Date: Mon, 2 Nov 2026 23:59:03 +0000 Subject: [PATCH 7/7] take prime-runs from PyPI only Verifiers main requires prime-runs from PyPI instead of pinning a git commit, so with the submodule on main the SDK overrides go away. This also moves prime-sandboxes to 0.5.4, which verifiers main requires. Co-Authored-By: Claude Opus 5.5 --- deps/verifiers | 2 +- pyproject.toml | 3 --- uv.lock | 14 ++++++-------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/deps/verifiers b/deps/verifiers index 60290d436fc..9c93692252d 160000 --- a/deps/verifiers +++ b/deps/verifiers @@ -1 +1 @@ -Subproject commit 60290d436fcb4c2ad6dd7d0986bcf00ff2366031 +Subproject commit 9c93692252de9b4783760ea01708af945f97f11c diff --git a/pyproject.toml b/pyproject.toml index 4e8e10153aa..d3e4e9367c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -158,9 +158,6 @@ override-dependencies = [ "nvidia-cutlass-dsl==4.8.0", "torch>=2.13.0", "openenv-core", - # Overridden because the verifiers submodule pins prime-runs and prime-traces to an older commit. - "prime-runs[train]>=0.1.6", - "prime-traces>=0.0.12", ] # prime-envs packages are auto-discovered as workspace members, so new diff --git a/uv.lock b/uv.lock index 0dd54f024ca..4ad8119ba78 100644 --- a/uv.lock +++ b/uv.lock @@ -164,8 +164,6 @@ overrides = [ { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'", specifier = ">=9.15" }, { name = "nvidia-cutlass-dsl", specifier = "==4.8.0" }, { name = "openenv-core" }, - { name = "prime-runs", extras = ["train"], specifier = ">=0.1.6" }, - { name = "prime-traces", specifier = ">=0.0.12" }, { name = "tilelang", specifier = "==0.1.12" }, { name = "torch", marker = "sys_platform != 'linux'", specifier = ">=2.13.0" }, { name = "torch", marker = "sys_platform == 'linux'", specifier = ">=2.13.0", index = "https://download.pytorch.org/whl/cu130" }, @@ -5502,7 +5500,7 @@ train = [ [[package]] name = "prime-sandboxes" -version = "0.5.1" +version = "0.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiofiles" }, @@ -5514,9 +5512,9 @@ dependencies = [ { name = "pyqwest" }, { name = "tenacity" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/78/304ac19c37884f4e0346f8dd810b8e1490027adb8d0c33e9c904e3ee62ac/prime_sandboxes-0.5.1.tar.gz", hash = "sha256:5f2978bec15f2b9b93cdadec0d2b0d2e5722167fef5eb68138cad94ae3fc4b17", size = 158790, upload-time = "2026-10-26T14:55:45.529Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/1a/29540680002fc44a919c60f5788df5c21489c59e513c88690bf4a2b558cf/prime_sandboxes-0.5.4.tar.gz", hash = "sha256:9457fb320a6e93db5586206ecbf59c39ec660ee6ec0a6b29e9abbb7a768477a4", size = 162299, upload-time = "2026-11-02T22:58:47.458Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/48/4cf79a666cd2dd605df52e9d61410b4bdf7e322616ffdf9b40a318ed5de4/prime_sandboxes-0.5.1-py3-none-any.whl", hash = "sha256:d6960aaa0a2c9eed05f114f8061a29c23254b27a5c7de03978d767ec465e28d8", size = 85795, upload-time = "2026-10-26T14:55:43.718Z" }, + { url = "https://files.pythonhosted.org/packages/b0/de/d65072fca5efc7b5c51dc76873be9b7d3f092aaac473974a1779d217508e/prime_sandboxes-0.5.4-py3-none-any.whl", hash = "sha256:44c64aa97668eb2f4c0d1497698c5a9e2db19153e0f9c5133678eec6eec20148", size = 88039, upload-time = "2026-11-02T22:58:46.087Z" }, ] [[package]] @@ -8001,7 +7999,7 @@ dependencies = [ { name = "openai" }, { name = "orjson" }, { name = "prime-pydantic-config", extra = ["toml"] }, - { name = "prime-runs", extra = ["train"] }, + { name = "prime-runs" }, { name = "prime-sandboxes" }, { name = "prime-tunnel" }, { name = "pydantic" }, @@ -8042,8 +8040,8 @@ requires-dist = [ { name = "openenv", marker = "extra == 'openenv'", specifier = ">=0.4.1" }, { name = "orjson", specifier = ">=3.10.0" }, { name = "prime-pydantic-config", extras = ["toml"], specifier = ">=0.4.3" }, - { name = "prime-runs", git = "https://github.com/PrimeIntellect-ai/prime?subdirectory=packages%2Fprime-runs&rev=adf8d25c3e34df8e039674fae9e0e4bf9cee27f3" }, - { name = "prime-sandboxes", specifier = ">=0.5.1" }, + { name = "prime-runs", specifier = ">=0.1.5" }, + { name = "prime-sandboxes", specifier = ">=0.5.4" }, { name = "prime-tunnel", specifier = ">=0.1.14" }, { name = "pydantic", specifier = ">=2.12.3" }, { name = "pyzmq", specifier = ">=27.1.0" },