Skip to content

Add Mooncake hidden states extraction backend#836

Open
fynnsu wants to merge 13 commits into
mainfrom
mooncake-on-main
Open

Add Mooncake hidden states extraction backend#836
fynnsu wants to merge 13 commits into
mainfrom
mooncake-on-main

Conversation

@fynnsu

@fynnsu fynnsu commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Purpose

Adds a mooncake backend to the hs_connectors sub-package. This will enable training on multi-node setups, or single node without using disk as an intermediary.

Requires the mooncake-transfer-engine package.

Tests

Comprehensive testing is still in progress.

I have successfully completed a basic regression test training using this backend.

Checklist

I have filled in:

  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan/results, such as providing test command and pasting the results.
  • (Optional) The necessary documentation update.
  • I (a human) have written or reviewed the code in this pr to the best of my ability.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 14ed8c20-fd75-4d9a-8128-d3fa021d671c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Mooncake hidden-states flow

Layer / File(s) Summary
Mooncake storage contract
hs_connectors/src/hs_connectors/mooncake_store.py, tests/unit/data_generation/test_mooncake_store.py
Adds tensor storage with metadata, polling, deletion, eviction handling, and fake-store roundtrip tests.
Asynchronous KV extraction and writes
hs_connectors/src/hs_connectors/mooncake_hidden_states_connector.py
Adds KV-cache hidden-state extraction, scheduler metadata, asynchronous Mooncake writes, and completion tracking.
Transfer backend and application wiring
hs_connectors/src/hs_connectors/transfer.py, hs_connectors/src/hs_connectors/__init__.py, hs_connectors/pyproject.toml, pyproject.toml, src/speculators/data_generation/vllm_client.py
Registers the Mooncake backend, configures its dependencies and CLI options, exposes its public classes, and returns Mooncake handles.
Runtime configuration and E2E validation
tests/e2e/utils.py, tests/e2e/smoke/test_mooncake_*, src/speculators/train/trainer.py
Adds Mooncake options to launch helpers, initializes validation workers, and tests inference roundtrips and online training.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: adding a Mooncake hidden-states backend.
Description check ✅ Passed The description is clearly about adding a Mooncake backend and its testing plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mooncake-on-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mergify

mergify Bot commented Jul 21, 2026

Copy link
Copy Markdown

The quality checks have failed. Please run make style and make quality under
the root directory to address the lint failures. You will need to install the
dev optional install to get the required linting packages:
https://github.com/vllm-project/speculators/blob/main/CONTRIBUTING.md

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/e2e/smoke/test_mooncake_e2e_smoke.py (1)

1-135: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Apply Ruff formatting.

The Quality workflow currently fails because ruff format --check would reformat this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/smoke/test_mooncake_e2e_smoke.py` around lines 1 - 135, Apply Ruff
formatting to the entire test_mooncake_hidden_states_roundtrip module, using the
repository’s configured Ruff formatter so it passes ruff format --check.
Preserve the existing test behavior and logic while updating only formatting.

Source: Pipeline failures

🧹 Nitpick comments (2)
tests/unit/data_generation/test_mooncake_store.py (1)

15-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for MooncakeStoreConfig.from_dict field filtering.

The connector builds MooncakeStoreConfig from an untrusted extra-config dict via from_dict, which silently drops unknown keys. That contract has no direct test here.

✅ Example test to add
def test_from_dict_filters_unknown_keys():
    cfg = MooncakeStoreConfig.from_dict({"protocol": "rdma", "bogus": 1})
    assert cfg.protocol == "rdma"
    assert not hasattr(cfg, "bogus")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/data_generation/test_mooncake_store.py` around lines 15 - 18, Add
a unit test in the Mooncake store tests covering MooncakeStoreConfig.from_dict
with a valid protocol field and an unknown key, asserting the valid value is
retained and the unknown key is not present on the resulting configuration.
hs_connectors/src/hs_connectors/transfer.py (1)

233-346: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate CLI-arg wiring and hostname/config-resolution logic within MooncakeBackend.

add_train_args/add_launch_args define identical argument sets, and from_train_args/build_kv_transfer_config independently duplicate the hostname-resolution + MooncakeStoreConfig construction. Extracting shared helpers would reduce drift risk when new Mooncake options are added.

♻️ Sketch: shared helpers
+def _add_mooncake_args(parser: argparse.ArgumentParser) -> None:
+    parser.add_argument("--mooncake-master", type=str, default="127.0.0.1:50051", ...)
+    parser.add_argument("--mooncake-metadata-server", type=str, default="P2PHANDSHAKE", ...)
+    parser.add_argument("--mooncake-protocol", choices=["tcp", "rdma"], default="tcp", ...)
+
+def _resolve_mooncake_config(args: argparse.Namespace) -> "MooncakeStoreConfig":
+    local_hostname = os.environ.get("MOONCAKE_LOCAL_HOSTNAME") or socket.gethostbyname(socket.gethostname())
+    return MooncakeStoreConfig(
+        local_hostname=local_hostname,
+        metadata_server=args.mooncake_metadata_server,
+        master_server_address=args.mooncake_master,
+        protocol=args.mooncake_protocol,
+    )

Then add_train_args/add_launch_args call _add_mooncake_args(parser), and from_train_args/build_kv_transfer_config call _resolve_mooncake_config(args).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hs_connectors/src/hs_connectors/transfer.py` around lines 233 - 346, Extract
the duplicated Mooncake CLI option definitions from add_train_args and
add_launch_args into a shared _add_mooncake_args(parser) helper, and have both
methods delegate to it. Extract hostname resolution and MooncakeStoreConfig
construction from from_train_args and build_kv_transfer_config into a shared
_resolve_mooncake_config(args) helper, then reuse that config in both call sites
while preserving their existing behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hs_connectors/src/hs_connectors/mooncake_hidden_states_connector.py`:
- Around line 199-213: Update the completion handling around _req_futures and
done_sending so failed futures are not reported as successfully finished. When
future.exception() is non-null, propagate the failure through the connector’s
established error mechanism (or write the distinguishable store failure marker
used by get_sample), and only add the request ID to done_sending after a
successful write; preserve cleanup of the future and accumulated request state.
- Around line 88-91: Update the assertion in MooncakeHiddenStatesConnector to
validate that speculative_config specifies the extract_hidden_states method, not
merely that the configuration exists. Keep the error message aligned with this
explicit requirement.

In `@src/speculators/train/trainer.py`:
- Around line 618-627: Move the eager val_loader iterator creation from the
post-model-setup portion of run_training() to before setup_model() initializes
or moves the model. Preserve the existing guards on val_loader, num_workers, and
_iterator so workers are spawned only when needed.

In `@tests/e2e/smoke/test_mooncake_e2e_smoke.py`:
- Around line 26-31: Skip the Mooncake smoke tests when the underlying Mooncake
dependency is unavailable: in tests/e2e/smoke/test_mooncake_e2e_smoke.py, add
pytest.importorskip("mooncake.store", ...) before importing
hs_connectors.mooncake_store; in
tests/e2e/smoke/test_mooncake_online_training.py, add the same guard before
starting mooncake_master. Keep the existing connector import and startup flow
unchanged after the dependency checks.

---

Outside diff comments:
In `@tests/e2e/smoke/test_mooncake_e2e_smoke.py`:
- Around line 1-135: Apply Ruff formatting to the entire
test_mooncake_hidden_states_roundtrip module, using the repository’s configured
Ruff formatter so it passes ruff format --check. Preserve the existing test
behavior and logic while updating only formatting.

---

Nitpick comments:
In `@hs_connectors/src/hs_connectors/transfer.py`:
- Around line 233-346: Extract the duplicated Mooncake CLI option definitions
from add_train_args and add_launch_args into a shared _add_mooncake_args(parser)
helper, and have both methods delegate to it. Extract hostname resolution and
MooncakeStoreConfig construction from from_train_args and
build_kv_transfer_config into a shared _resolve_mooncake_config(args) helper,
then reuse that config in both call sites while preserving their existing
behavior.

In `@tests/unit/data_generation/test_mooncake_store.py`:
- Around line 15-18: Add a unit test in the Mooncake store tests covering
MooncakeStoreConfig.from_dict with a valid protocol field and an unknown key,
asserting the valid value is retained and the unknown key is not present on the
resulting configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47b26def-a3cf-4544-97e2-cf5a47f42319

📥 Commits

Reviewing files that changed from the base of the PR and between 5cecdc8 and 0136f57.

📒 Files selected for processing (12)
  • hs_connectors/pyproject.toml
  • hs_connectors/src/hs_connectors/__init__.py
  • hs_connectors/src/hs_connectors/mooncake_hidden_states_connector.py
  • hs_connectors/src/hs_connectors/mooncake_store.py
  • hs_connectors/src/hs_connectors/transfer.py
  • pyproject.toml
  • src/speculators/data_generation/vllm_client.py
  • src/speculators/train/trainer.py
  • tests/e2e/smoke/test_mooncake_e2e_smoke.py
  • tests/e2e/smoke/test_mooncake_online_training.py
  • tests/e2e/utils.py
  • tests/unit/data_generation/test_mooncake_store.py

Comment thread hs_connectors/src/hs_connectors/mooncake_hidden_states_connector.py Outdated
Comment thread hs_connectors/src/hs_connectors/mooncake_hidden_states_connector.py
Comment thread src/speculators/train/trainer.py Outdated
Comment thread tests/e2e/smoke/test_mooncake_roundtrip.py
@mergify mergify Bot removed the quality-failed label Jul 21, 2026
@fynnsu
fynnsu force-pushed the mooncake-on-main branch from 9d96249 to 9e726ff Compare July 21, 2026 19:02

@speculatorsbot speculatorsbot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Design looks good -- the Mooncake connector follows the ExampleHiddenStatesConnector patterns closely (group/block-size resolution via spec, scheduler-side pending-saves, worker-side async extraction) and the backend registration through HiddenStatesBackend.register is clean. One missing safety check below.

🤖 Generated with Claude Code using the /pr-review skill

Comment thread hs_connectors/src/hs_connectors/mooncake_hidden_states_connector.py
fynnsu added 12 commits July 22, 2026 18:12
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
@fynnsu
fynnsu force-pushed the mooncake-on-main branch from a1935e6 to 45d6062 Compare July 22, 2026 18:14
Signed-off-by: Fynn Schmitt-Ulms <fschmitt@redhat.com>
@fynnsu fynnsu added the ready This PR is ready for review label Jul 22, 2026

@WindChimeRan WindChimeRan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I love the direction of single node without using disk as an intermediary and I think we should use mooncake as the default.

However, I couldn't get it runnable (our A100 machine):

My claude code's diagnosis:

0.3.12's prebuilt mooncake_master segfaults on startup, and the last working master (0.3.11) is manylinux_2_35-only — so mooncake can't run on glibc-2.34 hosts (RHEL 9); please ship a fixed master on a lower-glibc wheel or pin a known-good version.


Also Please run some benchmarks to show the speedup from the saving of the original disk lock data transfer.

@mergify

mergify Bot commented Jul 24, 2026

Copy link
Copy Markdown

Merge Protections

🔴 1 of 1 protections blocking · waiting on 👀 reviews

Protection Waiting on
🔴 Require approval from approved reviewers list 👀 reviews

🔴 Require approval from approved reviewers list

Waiting for any of

  • approved-reviews-by = dsikka
  • approved-reviews-by = fynnsu
  • approved-reviews-by = orestis-z
  • approved-reviews-by = rahul-tuli
  • approved-reviews-by = shanjiaz
This rule is failing.

All pull requests must have at least one approving review from a member of the approved reviewers list before merging.

  • any of:
    • approved-reviews-by = dsikka
    • approved-reviews-by = fynnsu
    • approved-reviews-by = orestis-z
    • approved-reviews-by = rahul-tuli
    • approved-reviews-by = shanjiaz

@rahul-tuli

Copy link
Copy Markdown
Collaborator

@fynnsu #841 just landed — the Mooncake backend needs its args mirrored into the schema 👋

Heads up: #841 (config-file-first CLI) is now in main. It changes how scripts/train.py gets its flags — instead of calling each backend's add_train_args, the CLI is now generated from a pydantic schema (src/speculators/train/config/schema.py). hs_connectors stays argparse-based and standalone (so vLLM can keep using it), so each backend's train-args are now mirrored as schema fields.

This PR doesn't touch scripts/train.py, so it'll rebase onto main with no git conflict — but it'll be quietly broken until the mirror is added:

  • tests/unit/train/config/test_backend_reconciliation.py will fail — it asserts every dest a backend registers via add_train_args has a matching schema field.
  • At runtime the --mooncake-* flags won't exist (train.py's parser is schema-generated now), and MooncakeBackend.from_train_args reading args.mooncake_* would hit an AttributeError.

The fix — mirror the 3 args into the schema

Your MooncakeBackend.add_train_args / from_train_args stay exactly as-is. You just need the same three knobs mirrored as schema fields so they survive resolution (field names must equal the argparse dests: mooncake_master, mooncake_metadata_server, mooncake_protocol). Two ways to do it — pick based on how many backends we expect. Note the group only affects --help grouping and run.yaml nesting; the flag names, flat dests, from_train_args, and the reconciliation test are identical either way.

Option A — mirror into DataArgs (minimal, matches today's precedent). Drop them next to the existing hidden_states_path (the file backend's mirror already lives there):

# src/speculators/train/config/schema.py — in class DataArgs
    mooncake_master: str = Field(
        default="127.0.0.1:50051",
        description="Mooncake master server address. "
        "Used with --hidden-states-backend mooncake.",
    )
    mooncake_metadata_server: str = Field(
        default="P2PHANDSHAKE",
        description="Mooncake metadata server (or P2PHANDSHAKE). "
        "Used with --hidden-states-backend mooncake.",
    )
    mooncake_protocol: Literal["tcp", "rdma"] = Field(
        default="tcp",
        description="Mooncake transport protocol. "
        "Used with --hidden-states-backend mooncake.",
    )

Cheapest change, but DataArgs (dataset + dataloader config) starts to accumulate unrelated transport config as more backends land.

Option B — a dedicated BackendArgs group (cleaner if more backends are coming). Collect all backend-mirrored args in one group and move hidden_states_path in too, so the group is the single home for the mirror:

# src/speculators/train/config/schema.py
class BackendArgs(_Group):
    """Per-backend transport args, mirrored from each HiddenStatesBackend's
    add_train_args (hs_connectors stays argparse-based). Only the fields for the
    selected --hidden-states-backend apply."""
    hidden_states_path: str | None = Field(default=None, ...)      # 'file' (moved from DataArgs)
    mooncake_master: str = Field(default="127.0.0.1:50051", ...)   # 'mooncake'
    mooncake_metadata_server: str = Field(default="P2PHANDSHAKE", ...)
    mooncake_protocol: Literal["tcp", "rdma"] = Field(default="tcp", ...)

# register in _GROUPS (order defines --help + run.yaml layout), right after "data"
    "data": DataArgs,
    "backend": BackendArgs,

# and add the field on TrainConfig
    backend: BackendArgs = Field(default_factory=BackendArgs)

More churn on the just-landed schema, and it shifts hidden_states_path's run.yaml nesting from data:backend: (trivial break — run.yaml only just shipped), but it's the natural "one group per concern" home and scales for the NIXL/RDMA backends on the roadmap.

My take: since the reconciliation test itself notes NIXL/RDMA are coming, I'd lean B (it's cheapest to extract now, with only one field to move) — but A is totally fine if you want to keep this PR minimal; just file a follow-up to extract BackendArgs when the second backend lands. Either way:

  • Literal["tcp", "rdma"] auto-generates the argparse choices=["tcp", "rdma"], matching your --mooncake-protocol. (Literal is already imported in schema.py.)
  • No change needed for --hidden-states-backend mooncake — its choices come from the live HiddenStatesBackend.registry, so registering the backend makes mooncake a valid choice automatically.
  • Neither option gates the flags (warn if --mooncake-* is set under --hidden-states-backend file). That'd mirror _ALGORITHM_GROUP_USERS / _warn_mismatched_algorithm_blocks; B is the natural foundation for it. Worth a follow-up, not needed here.

Sanity check after adding them

python -m pytest tests/unit/train/config/test_backend_reconciliation.py
python scripts/train.py --help | grep mooncake   # all three flags show up

Ping me if you'd like the change as a ready-to-apply patch. Or if you'd like me to push the commit

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

Labels

ready This PR is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants