| 1 | # SPDX-License-Identifier: Apache-2.0 |
| 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | """Configuration for Moondream3 model.""" |
| 4 | |
| 5 | from transformers import PreTrainedConfig |
| 6 | |
| 7 | |
| 8 | class Moondream3VisionConfig(PreTrainedConfig): |
| 9 | """Vision encoder configuration for Moondream3.""" |
| 10 | |
| 11 | model_type = "moondream3_vision" |
| 12 | |
| 13 | def __init__( |
| 14 | self, |
| 15 | enc_dim: int = 1152, |
| 16 | enc_patch_size: int = 14, |
| 17 | enc_n_layers: int = 27, |
| 18 | enc_ff_dim: int = 4304, |
| 19 | enc_n_heads: int = 16, |
| 20 | proj_inner_dim: int = 8192, |
| 21 | crop_size: int = 378, |
| 22 | max_crops: int = 12, |
| 23 | overlap_margin: int = 4, |
| 24 | **kwargs, |
| 25 | ): |
| 26 | super().__init__(**kwargs) |
| 27 | self.enc_dim = enc_dim |
| 28 | self.enc_patch_size = enc_patch_size |
| 29 | self.enc_n_layers = enc_n_layers |
| 30 | self.enc_ff_dim = enc_ff_dim |
| 31 | self.enc_n_heads = enc_n_heads |
| 32 | self.proj_inner_dim = proj_inner_dim |
| 33 | self.crop_size = crop_size |
| 34 | self.max_crops = max_crops |
| 35 | self.overlap_margin = overlap_margin |
| 36 | |
| 37 | # Standard HuggingFace attributes for vision config |
| 38 | self.hidden_size = enc_dim |
| 39 | self.num_attention_heads = enc_n_heads |
| 40 | self.num_hidden_layers = enc_n_layers |
| 41 | self.intermediate_size = enc_ff_dim |
| 42 | self.patch_size = enc_patch_size |
| 43 | self.image_size = crop_size |
| 44 | |
| 45 | |
| 46 | class Moondream3TextConfig(PreTrainedConfig): |
| 47 | """Text decoder configuration for Moondream3.""" |
| 48 | |
| 49 | model_type = "moondream3_text" |
| 50 | |
| 51 | def __init__( |
| 52 | self, |
| 53 | dim: int = 2048, |
| 54 | ff_dim: int = 8192, |
| 55 | n_layers: int = 24, |
| 56 | vocab_size: int = 51200, |
| 57 | max_context: int = 4096, |
| 58 | n_heads: int = 32, |
| 59 | n_kv_heads: int = 32, |
| 60 | prefix_attn: int = 730, |
| 61 | rope_theta: float = 1500000.0, |
| 62 | moe: dict | None = None, |
| 63 | **kwargs, |
| 64 | ): |
| 65 | super().__init__(**kwargs) |
| 66 | |
| 67 | # Store original moondream3 config names |
| 68 | self.dim = dim |
| 69 | self.ff_dim = ff_dim |
| 70 | self.n_layers = n_layers |
| 71 | self.n_heads = n_heads |
| 72 | self.n_kv_heads = n_kv_heads |
| 73 | self.prefix_attn = prefix_attn |
| 74 | self.max_context = max_context |
| 75 | self.rope_theta = rope_theta |
| 76 | |
| 77 | # MoE config |
| 78 | moe = moe or {} |
| 79 | self.moe_start_layer = moe.get("start_layer", 4) |
| 80 | self.moe_num_experts = moe.get("n_experts", 64) |
| 81 | self.moe_experts_per_token = moe.get("n_experts_per_tok", 8) |
| 82 | self.moe_expert_inner_dim = moe.get("expert_inner_dim", 1024) |
| 83 | |
| 84 | # Standard HuggingFace attributes (required by vLLM) |
| 85 | self.hidden_size = dim |
| 86 | self.num_attention_heads = n_heads |
| 87 | self.num_key_value_heads = n_kv_heads |
| 88 | self.num_hidden_layers = n_layers |
| 89 | self.intermediate_size = ff_dim |
| 90 | self.vocab_size = vocab_size |
| 91 | self.max_position_embeddings = max_context |
| 92 | |
| 93 | # Moondream3 uses token 0 (<|endoftext|>) as both BOS and EOS. |
| 94 | # Token 3 (<|md_reserved_2|>) is an answer delimiter that the model |
| 95 | # implementation suppresses during generation. |
| 96 | self.bos_token_id = 0 |
| 97 | self.eos_token_id = 0 |
| 98 | |
| 99 | # MoE standard attributes |
| 100 | self.num_local_experts = self.moe_num_experts |
| 101 | self.num_experts_per_tok = self.moe_experts_per_token |
| 102 | |
| 103 | |
| 104 | class Moondream3Config(PreTrainedConfig): |
| 105 | """Combined configuration for Moondream3 multimodal model.""" |
| 106 | |
| 107 | model_type = "moondream3" |
| 108 | is_composition = True |
| 109 | |
| 110 | def __init__( |
| 111 | self, |
| 112 | config: dict | None = None, |
| 113 | **kwargs, |
| 114 | ): |
| 115 | config = config or {} |
| 116 | |
| 117 | # Parse text config |
| 118 | text_config = config.get("text", {}) |
| 119 | self.text_config: Moondream3TextConfig = Moondream3TextConfig(**text_config) |
| 120 | |
| 121 | # Parse vision config |
| 122 | vision_config = config.get("vision", {}) |
| 123 | self.vision_config = Moondream3VisionConfig(**vision_config) |
| 124 | |
| 125 | # Store the original config dict for model access |
| 126 | self.config = config |
| 127 | tokenizer_config = config.get("tokenizer", {}) |
| 128 | self.answer_token_id = tokenizer_config.get("answer_id", 3) |
| 129 | |
| 130 | super().__init__(**kwargs) |
| 131 | |
| 132 | # Expose key attributes at top level for vLLM compatibility |
| 133 | self.hidden_size = self.text_config.hidden_size |
| 134 | self.num_attention_heads = self.text_config.num_attention_heads |
| 135 | self.num_key_value_heads = self.text_config.num_key_value_heads |
| 136 | self.num_hidden_layers = self.text_config.num_hidden_layers |
| 137 | self.vocab_size = self.text_config.vocab_size |
| 138 | self.intermediate_size = self.text_config.intermediate_size |
| 139 | |
| 140 | # Moondream3 uses token 0 (<|endoftext|>) as both BOS and EOS. |
| 141 | # Token 3 (<|md_reserved_2|>) is an answer delimiter that the model |
| 142 | # implementation suppresses during generation. |
| 143 | self.bos_token_id = 0 |
| 144 | self.eos_token_id = 0 |
| 145 | |
| 146 | def get_text_config(self, decoder: bool = False) -> "Moondream3TextConfig": |
| 147 | """Return the text config for vLLM's text_config detection. |
| 148 | |
| 149 | Args: |
| 150 | decoder: Ignored. Only used for encoder-decoder models. |
| 151 | |
| 152 | """ |
| 153 | return self.text_config |