Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions tests/test_mllm_continuous_batching.py
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,13 @@ def __init__(self):

install_mtp_mllm(batch_gen, language_model, num_draft_tokens=4)

stats = batch_gen.get_mtp_stats()
assert stats["enabled"] is True
assert stats["requested_draft_tokens"] == 4
assert stats["effective_draft_tokens"] == 1
assert stats["attempted"] == 0
assert "non_greedy_sampling" in stats["bypass_reasons"]

logits_processor = MagicMock()
tokens, logprobs = batch_gen._step(
mx.array([[123]]),
Expand All @@ -1021,6 +1028,7 @@ def __init__(self):
original_step.assert_called_once()
language_model.assert_not_called()
language_model.mtp_forward.assert_not_called()
assert batch_gen.get_mtp_stats()["attempted"] == 0

def test_install_mtp_mllm_disables_mtp_for_non_greedy_sampling(self):
from vllm_mlx.mllm_batch_generator import install_mtp_mllm
Expand Down Expand Up @@ -1130,6 +1138,9 @@ def __call__(self, verify_input, cache=None, return_hidden=False):
assert [r.token for r in responses] == [1, 2]
assert request_sampler.call_count == 1
assert batch_gen.sampler.call_count == 0
assert batch_gen.get_mtp_stats()["attempted"] == 1
assert batch_gen.get_mtp_stats()["accepted"] == 1
assert batch_gen.get_mtp_stats()["acceptance_rate"] == 1.0

def test_next_keeps_retired_processors_by_default(self, monkeypatch):
from vllm_mlx.mllm_batch_generator import (
Expand Down
7 changes: 6 additions & 1 deletion vllm_mlx/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,12 @@ def serve_command(args):
if args.chunked_prefill_tokens > 0:
print(f"Chunked prefill: {args.chunked_prefill_tokens} tokens per step")
if args.enable_mtp:
print(f"MTP: enabled, draft_tokens={args.mtp_num_draft_tokens}")
print(f"MTP: enabled, requested_draft_tokens={args.mtp_num_draft_tokens}")
if args.mllm:
print(
"MTP: MLLM path currently uses effective_draft_tokens=1 "
"per verify step; inspect /v1/status for attempts and acceptance"
)
print(f"Stream interval: {args.stream_interval} tokens")
if args.use_paged_cache:
print(
Expand Down
1 change: 1 addition & 0 deletions vllm_mlx/engine/batched.py
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ def get_stats(self) -> dict[str, Any]:
"paged_cache",
"prefix_cache",
"batch_generator",
"mtp",
"requests",
):
if key in mllm_stats:
Expand Down
30 changes: 28 additions & 2 deletions vllm_mlx/mllm_batch_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1973,8 +1973,33 @@ def install_mtp_mllm(
# Deferred drafts keyed by UID
_deferred_drafts: Dict[int, dict] = {}

# MTP stats
_mtp_stats = {"accepted": 0, "rejected": 0, "errors": 0}
# MTP stats. These are intentionally exposed through get_mtp_stats() so
# /v1/status can distinguish "weights injected" from useful draft work.
_mtp_stats = {"attempted": 0, "accepted": 0, "rejected": 0, "errors": 0}

def _get_mtp_stats() -> Dict[str, Any]:
verified = _mtp_stats["accepted"] + _mtp_stats["rejected"]
acceptance_rate = _mtp_stats["accepted"] / verified if verified > 0 else 0.0
return {
"enabled": True,
"requested_draft_tokens": num_draft_tokens,
"effective_draft_tokens": 1,
"mode": "always_advance_verified",
"attempted": _mtp_stats["attempted"],
"accepted": _mtp_stats["accepted"],
"rejected": _mtp_stats["rejected"],
"errors": _mtp_stats["errors"],
"acceptance_rate": acceptance_rate,
"bypass_reasons": {
"prefill": "input_tokens.shape[1] > 1",
"no_active_batch": "active_batch is None",
"concurrent_batch": "len(active_batch) > 1",
"non_greedy_sampling": "temperature/top_p/top_k/min_p not greedy",
"logits_processors": "request-local logits processors active",
},
}

batch_gen.get_mtp_stats = _get_mtp_stats

def _mtp_step(
input_tokens: mx.array,
Expand Down Expand Up @@ -2067,6 +2092,7 @@ def _mtp_step(

# MTP draft + always-advance verify
try:
_mtp_stats["attempted"] += 1
draft_logits = language_model.mtp_forward(
hidden_states[:, -1:, :],
primary_tokens[:, None],
Expand Down
2 changes: 2 additions & 0 deletions vllm_mlx/mllm_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,8 @@ def get_stats(self) -> Dict[str, Any]:
# Vision embedding cache stats from batch generator
vec_stats = self.batch_generator.get_vision_cache_stats()
stats["vision_embedding_cache"] = vec_stats
if hasattr(self.batch_generator, "get_mtp_stats"):
stats["mtp"] = self.batch_generator.get_mtp_stats()

# Include Metal memory stats
try:
Expand Down
1 change: 1 addition & 0 deletions vllm_mlx/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3025,6 +3025,7 @@ async def status():
"cache": stats.get("memory_aware_cache")
or stats.get("paged_cache")
or stats.get("prefix_cache"),
"mtp": stats.get("mtp"),
"requests": stats.get("requests", []),
}

Expand Down
Loading