Support v1 raw multimodal image offload#2836
Open
eligotts wants to merge 27 commits into
Open
Conversation
This was referenced Jun 20, 2026
a574785 to
4e78f06
Compare
4e78f06 to
2f5eacc
Compare
2f5eacc to
034e83e
Compare
bfc45cc to
b7903f6
Compare
/inference/v1/generate materializes every raw ref (no cache-only None branch); an unresolved ref is a hard error, not a silent None. Removes the cache-miss 409 path + helpers (_cache_only_mm_hashes/_is_missing_mm_cache_error/_missing_mm_cache_message). Bumps renderers/verifiers submodules to the matching cleanup commits. Deployment-agnostic; mm_hash encoder cache still skips re-encode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
renderers.mm_store dropped the split_mmraw_ref backcompat alias; use split_raw_mm_ref. Bump renderers submodule pin to the cleanup commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # deps/verifiers # src/prime_rl/orchestrator/orchestrator.py # src/prime_rl/trainer/batch.py # tests/unit/orchestrator/test_batch.py
# Conflicts: # packages/prime-rl-configs/src/prime_rl/configs/inference.py # packages/prime-rl-configs/src/prime_rl/configs/rl.py # src/prime_rl/entrypoints/rl.py
samsja
reviewed
Jun 29, 2026
| max_concurrent_runs: int = Field(1, ge=1) | ||
| """Maximum number of concurrent runs to allow. If 1, only one run may run at a time.""" | ||
|
|
||
| missing_mm_image_policy: MissingMMImagePolicy = "placeholder_zero_loss" |
Member
There was a problem hiding this comment.
can we put this in the multi modal config ?
samsja
reviewed
Jun 29, 2026
Comment on lines
+46
to
+68
| def build_run_asset_env( | ||
| output_dir: Path, | ||
| multimodal: MultimodalConfig | None = None, | ||
| base: Mapping[str, str] | None = None, | ||
| ) -> dict[str, str]: | ||
| """Resolve the environment used by subprocesses that share run image assets. | ||
|
|
||
| Prime-RL config owns the multimodal image offload path. Env vars are only the | ||
| transport used by verifiers/renderers running in subprocesses. | ||
| """ | ||
|
|
||
| env = dict(os.environ if base is None else base) | ||
| config = multimodal or MultimodalConfig() | ||
|
|
||
| env[IMAGE_OFFLOAD_DIR_ENV] = str(resolve_image_offload_dir(output_dir, config, env)) | ||
|
|
||
| return env | ||
|
|
||
|
|
||
| def apply_run_asset_env(output_dir: Path, multimodal: MultimodalConfig) -> None: | ||
| if os.environ.get(IMAGE_OFFLOAD_DIR_ENV): | ||
| return | ||
| os.environ.update(build_run_asset_env(output_dir, multimodal=multimodal)) |
Member
There was a problem hiding this comment.
not a fan of seting up env var, why can't we jsut read from the env var instead and dinamycally change it in the code if the env var is not present ?
samsja
reviewed
Jun 29, 2026
Comment on lines
+129
to
+130
| inherited_env = dict(os.environ) | ||
| writer_run_asset_env = build_run_asset_env(config.orchestrator.output_dir, multimodal=config.multimodal) |
# Conflicts: # deps/verifiers
- Kimi adapter reads the actual processor layout instead of hard-equating it to the renderer constant; the fingerprint comparison is the single drift detector, matching the Qwen adapter. - Orchestrator validates every image ref's placeholder span lands on image-typed tokens before a sample ships, so offset drift anywhere upstream fails loudly instead of silently truncating wrong. - FakeDataLoader carries the mm stat counters; trainer metrics read them directly. Drop the duplicate apply_run_asset_env in the entrypoint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
renderers: bridge sidecar aliasing fix, HF smart_resize import, full-hash asset filenames, layout parity tests. verifiers: ingress offload covers every image part shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a13a214. Configure here.
# Conflicts: # deps/renderers # deps/verifiers # packages/prime-rl-configs/src/prime_rl/configs/inference.py # packages/prime-rl-configs/src/prime_rl/configs/orchestrator.py # src/prime_rl/orchestrator/trajectories.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

Summary
Wires Prime-RL v1 training onto the raw multimodal image offload contract used by the companion Renderers and Verifiers PRs.
Companion PRs:
Current design:
VF_RENDERER_IMAGE_OFFLOAD_DIR; they rewrite image parts to sharedfile://assets and emit JSON-safe raw descriptors.raw_image_urias the sole image locator plus adapter-owned metadata (family,layout_fingerprint,payload).raw_image_idwas removed to avoid two sources of truth.mmraw:<base64-json-payload>refs carryingraw_image_uri, hash, family, fingerprint, modality, and payload. Raw-mm ref and descriptor version markers were removed because this branch has no compatibility contract yet.VF_RENDERER_IMAGE_OFFLOAD_DIR.file://URIs throughMMRefs;build_mm_refsnow builds those URIs from the raw descriptors instead of rescanning messages.Noneslots, inline base64 storage, and root-plus-id lookup paths are not supported.Submodule pins:
a49e0fc(branch merged with renderersmainat5904fa2)9bc3cc36(branch merged with verifiersmainat5885ab9c)Latest merge (
7ea71a96f): merged prime-rlorigin/main(22 commits, incl. the dispatch-gate fix #2937, 1-indexed steps #2896, vLLM 0.24 #2921, and the ExperimentalConfig removal #2931 — resolved in favor of keeping[multimodal]without the placeholders). Both dep submodules were merged with their ownmainand re-pinned.Review hardening (latest push)
MMRefsrestructured from a modality-keyed descriptor dict + parallel lists toimages: list[MMImageRef](descriptor, hash, uri, placeholder range per image). Alignment is structural, so the count validators collapsed to one zip atbuild_mm_refs; truncation keeps a prefix of whole images and never splits a placeholder.build_mm_refsenforces sorted, non-overlapping placeholders; the orchestrator additionally validates every image ref's placeholder span lands on image-typed tokens (mm_token_type_ids == 1) before a sample ships, so offset drift anywhere upstream fails loudly.image_urlnested/direct,imagedirect, typed pydantic parts); renderers hard-requirefile://sources.Validation
uv run ruff check renderers/configs.py renderers/base.py renderers/client.py renderers/qwen3_vl.py renderers/qwen35.py renderers/kimi_k25.py renderers/mm_store.py renderers/__init__.py tests/test_renderer_config.py tests/test_multimodal_output_modes.py tests/test_client.pyuv run ruff format --check renderers/configs.py renderers/base.py renderers/client.py renderers/qwen3_vl.py renderers/qwen35.py renderers/kimi_k25.py renderers/mm_store.py renderers/__init__.py tests/test_renderer_config.py tests/test_multimodal_output_modes.py tests/test_client.pyuv run pytest tests/test_renderer_config.py tests/test_client.py::test_generate_serializes_raw_mm_refs tests/test_multimodal_output_modes.py -q->38 passedruff check,ruff format,ty (ci parity)passeduv run ruff check src/prime_rl/entrypoints/rl.py src/prime_rl/inference/server.py src/prime_rl/inference/vllm/serving_tokens.py src/prime_rl/multimodal/schema.py src/prime_rl/orchestrator/trajectories.py src/prime_rl/utils/mm.py tests/unit/inference/test_serving_tokens.py tests/unit/orchestrator/test_batch.py tests/unit/utils/test_mm.pyuv run pytest tests/unit/utils/test_mm.py tests/unit/inference/test_serving_tokens.py::test_materialize_raw_image_ref_uses_generic_family_payload tests/unit/orchestrator/test_batch.py::test_prepare_sample_rejects_overlong_raw_mm_refs->4 passeduv run pytest tests/unit/inference/test_serving_tokens.py::test_materialize_raw_image_ref_uses_generic_family_payload->1 passedgit diff --check,uv lock --check, anduv sync --all-extras --lockedpassed on the branch after mergingorigin/main.2182 passed(incl. Kimi-K2.5 parity/byte-parity against the pinned processor), verifiers839 passed(pre-existingtest_envs/test_opencode_rlm_envfailures excluded, reproduced on the base branch), prime-rltests/unitminus GPU-onlytests/unit/train410 passed.Latest sync:
/home/ubuntu/prime-rl-mainand merged latest prime-rlorigin/main(f19ba721a) into this branch.9b3e7ee, which contains both this PR's previous Verifiers pin (22c7cf4c) and current main's Verifiers pin (22d6333a). This resolves thedeps/verifiersmerge conflict and satisfieslean-v1'sverifiers>=0.1.15.dev402requirement.a7953b9, which contains main's renderers pin (a5efbb), the processed multimodal renderer output mode, and the cleaned lockfile diff.uv.lockfor the optional Renderersvisionextra metadata so CI'suv sync --all-extras --lockedpath is in sync.Note
High Risk
Touches multimodal data path across orchestrator, trainer, and vLLM inference with new transport contract and hash/fingerprint validation; misconfiguration or companion renderer/verifiers drift would break rollouts or training silently without the new guards.
Overview
Introduces v1 raw multimodal image offload for RL: images live as shared
file://assets under a run-scoped directory, and rollouts carry lightweight raw descriptors (MMRefs/MMImageRef) instead of encodedmm_kwargstensors.Config & launcher: New shared
[multimodal].offload_dirpropagates to trainer, orchestrator, and inference. The RL launcher resolves the path, exportsVF_RENDERER_IMAGE_OFFLOAD_DIRto the orchestrator (protected fromenv_varsoverride), and wires SLURM templates. Trainer addsmissing_mm_image_policy(placeholder_zero_lossvserror).Transport & orchestrator:
trace_to_samplesbuildsmm_refsviabuild_mm_refs, validates image placeholder spans againstmm_token_type_ids, and rejects legacy processedmm_kwargs. Truncation uses placeholder boundaries on refs, not pixel tensor slicing.Trainer:
RawImageMaterializerand Qwen VL / Kimi K2.5 adapters materialize images at batch time (hash + layout fingerprint checks). Missing files can synthesize zero-loss placeholders and mask the microbatch. Forward uses per-familyForwardPolicyforposition_ids/mm_token_type_ids.Inference:
PrimeRlServingTokensmaterializes raw refs from request payloads (verified read + adapter) before vLLM, returninginvalid_mm_image_ref400s on mismatch.Docs and monitor skill describe offload debugging; the old Qwen3-VL features roundtrip e2e test is removed.
Reviewed by Cursor Bugbot for commit 1f290dc. Bugbot is set up for automated code reviews on this repo. Configure here.