deepseek_v4.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. # SPDX-License-Identifier: Apache-2.0
  2. # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
  3. import copy
  4. from typing import Any
  5. from transformers import PreTrainedTokenizerFast
  6. from vllm.entrypoints.chat_utils import ChatCompletionMessageParam
  7. from .deepseek_v4_encoding import encode_messages
  8. from .hf import HfTokenizer, get_cached_tokenizer
  9. from .protocol import TokenizerLike
  10. def get_deepseek_v4_tokenizer(tokenizer: HfTokenizer) -> HfTokenizer:
  11. """
  12. Wraps a tokenizer to use the custom DeepSeek V4 chat template encoding.
  13. """
  14. dsv4_tokenizer = copy.copy(tokenizer)
  15. added_vocab = tokenizer.get_added_vocab()
  16. added_vocab_size = len(added_vocab)
  17. tokenizer_vocab_size = tokenizer.vocab_size
  18. class _DeepseekV4Tokenizer(tokenizer.__class__): # type: ignore
  19. def apply_chat_template(
  20. self,
  21. messages: list["ChatCompletionMessageParam"],
  22. tools: list[dict[str, Any]] | None = None,
  23. **kwargs,
  24. ) -> str | list[int]:
  25. thinking = kwargs.get("thinking", False)
  26. enable_thinking = kwargs.get("enable_thinking", False)
  27. thinking = thinking or enable_thinking
  28. thinking_mode = "thinking" if thinking else "chat"
  29. conversation = kwargs.get("conversation", messages)
  30. messages = conversation.copy()
  31. if tools is not None and len(tools) > 0:
  32. messages.insert(0, {"role": "system"})
  33. messages[0]["tools"] = tools # type: ignore[typeddict-unknown-key]
  34. reasoning_effort = kwargs.get("reasoning_effort")
  35. if not isinstance(reasoning_effort, str):
  36. reasoning_effort = None
  37. elif reasoning_effort == "none":
  38. thinking_mode = "chat"
  39. reasoning_effort = None
  40. elif reasoning_effort in ("max", "xhigh"):
  41. reasoning_effort = "max"
  42. elif reasoning_effort not in ("low", "high", "max"):
  43. reasoning_effort = None
  44. encode_config = dict(
  45. thinking_mode=thinking_mode,
  46. drop_thinking=kwargs.get("drop_thinking", True),
  47. reasoning_effort=reasoning_effort,
  48. )
  49. prompt_str = encode_messages(messages, **encode_config) # type: ignore
  50. if kwargs.get("tokenize", True):
  51. tokenizer_kwargs = {
  52. k: kwargs[k] for k in ("truncation", "max_length") if k in kwargs
  53. }
  54. return self.encode(
  55. prompt_str,
  56. add_special_tokens=False,
  57. **tokenizer_kwargs,
  58. )
  59. return prompt_str
  60. def num_special_tokens_to_add(self) -> int:
  61. return len(self.encode(""))
  62. def __len__(self) -> int:
  63. return tokenizer_vocab_size + added_vocab_size
  64. def get_added_vocab(self) -> dict[str, int]:
  65. return added_vocab.copy()
  66. def __reduce__(self):
  67. return get_deepseek_v4_tokenizer, (tokenizer,)
  68. _DeepseekV4Tokenizer.__name__ = f"DSV4{tokenizer.__class__.__name__}"
  69. dsv4_tokenizer.__class__ = _DeepseekV4Tokenizer
  70. return dsv4_tokenizer
  71. class DeepseekV4Tokenizer(TokenizerLike):
  72. @classmethod
  73. def from_pretrained(cls, *args, **kwargs) -> HfTokenizer:
  74. tokenizer = PreTrainedTokenizerFast.from_pretrained(*args, **kwargs)
  75. return get_cached_tokenizer(get_deepseek_v4_tokenizer(tokenizer))