From 5556631789c8880159f69efaf783a62bf78b7be3 Mon Sep 17 00:00:00 2001 From: Jonathan Mamou Date: Tue, 30 Jun 2026 00:45:12 +0300 Subject: [PATCH] [Speculative Decoding] Validate vocabulary compatibility in STANDALONE mode (#23838) Co-authored-by: kpham-sgl Co-authored-by: Claude Opus 4.8 (1M context) --- .../srt/speculative/standalone_worker_v2.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/python/sglang/srt/speculative/standalone_worker_v2.py b/python/sglang/srt/speculative/standalone_worker_v2.py index dd6e9770d..106600357 100644 --- a/python/sglang/srt/speculative/standalone_worker_v2.py +++ b/python/sglang/srt/speculative/standalone_worker_v2.py @@ -193,6 +193,11 @@ class StandaloneWorkerV2(EAGLEWorkerV2): target_worker, ) + self._validate_vocab_compatibility( + target_vocab_size=target_worker.model_runner.model_config.vocab_size, + target_tokenizer=target_worker.tokenizer, + ) + # Some dummy tensors self.num_new_pages_per_topk = torch.empty( (), dtype=torch.int64, device=self.device @@ -203,3 +208,35 @@ class StandaloneWorkerV2(EAGLEWorkerV2): # TODO: Adaptive speculative self.adaptive_controller: Optional[AdaptiveController] = None + + def _validate_vocab_compatibility( + self, + target_vocab_size: int, + target_tokenizer, + ) -> None: + """Raise ValueError if the draft and target vocabularies are incompatible.""" + draft_vocab_size = self._draft_worker.draft_runner.model_config.vocab_size + draft_tokenizer = self._draft_worker.draft_worker.tokenizer + if target_vocab_size != draft_vocab_size: + raise ValueError( + f"STANDALONE speculative decoding requires the draft model to share the " + f"same vocabulary as the target model, but got " + f"target vocab_size={target_vocab_size} and " + f"draft vocab_size={draft_vocab_size}. " + f"Use a draft model with a matching vocabulary, or a speculative " + f"algorithm that supports heterogeneous vocabularies." + ) + if ( + target_tokenizer is not None + and draft_tokenizer is not None + and hasattr(target_tokenizer, "get_vocab") + and hasattr(draft_tokenizer, "get_vocab") + and target_tokenizer.get_vocab() != draft_tokenizer.get_vocab() + ): + raise ValueError( + "STANDALONE speculative decoding requires the draft model to share the " + "same vocabulary as the target model, but the two tokenizers have " + "different token-to-id mappings even though their vocab sizes match. " + "Use a draft model with a matching vocabulary, or a speculative " + "algorithm that supports heterogeneous vocabularies." + )