majentik commited on
Commit
6845655
·
verified ·
1 Parent(s): fb74978

feat: upload TurboQuant-MLX-3bit (card-twin of RotorQuant)

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ NVIDIA Open Model License Agreement
2
+ This NVIDIA Open Model License Agreement (the “Agreement”) is a legal agreement between the Legal Entity You represent, or if no
3
+ entity is identified, You and NVIDIA Corporation and its Affiliates (“NVIDIA”) and governs Your use of the Models that NVIDIA
4
+ provides to You under this Agreement. NVIDIA and You are each a “party” and collectively the “parties.”
5
+ NVIDIA models released under this Agreement are intended to be used permissively and enable the further development of AI
6
+ technologies. Subject to the terms of this Agreement, NVIDIA confirms that:
7
+
8
+
9
+ Models are commercially useable.
10
+
11
+
12
+
13
+ You are free to create and distribute Derivative Models.
14
+
15
+
16
+
17
+ NVIDIA does not claim ownership to any outputs generated using the Models or Model Derivatives.
18
+
19
+ By using, reproducing, modifying, distributing, performing or displaying any portion or element of the Model or Derivative Model, or
20
+ otherwise accepting the terms of this Agreement, you agree to be bound by this Agreement.
21
+ 1.
22
+
23
+ Definitions. The following definitions apply to this Agreement:
24
+
25
+ 1.1.
26
+
27
+ “Derivative Model” means all (a) modifications to the Model, (b) works based on the Model, and (c) any other derivative
28
+ works of the Model. An output is not a Derivative Model.
29
+
30
+ 1.2.
31
+
32
+ “Legal Entity” means the union of the acting entity and all other entities that control, are controlled by, or are under common
33
+ control with that entity. For the purposes of this definition, “control” means (a) the power, direct or indirect, to cause the
34
+ direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more
35
+ of the outstanding shares, or (c) beneficial ownership of such entity.
36
+
37
+ 1.3.
38
+
39
+ “Model” means the machine learning model, software, checkpoints, learnt weights, algorithms, parameters, configuration
40
+ files and documentation shared under this Agreement.
41
+
42
+ 1.4.
43
+
44
+ “You” or “Your” means an individual or Legal Entity exercising permissions granted by this Agreement.
45
+
46
+ 2.
47
+
48
+ Conditions for Use, License Grant, AI Ethics and IP Ownership.
49
+
50
+ 2.1.
51
+ Conditions for Use. The Model and any Derivative Model are subject to additional terms as described in Section 2 and
52
+ Section 3 of this Agreement and govern Your use. If You institute copyright or patent litigation against any entity (including a crossclaim or counterclaim in a lawsuit) alleging that the Model or a Derivative Model constitutes direct or contributory copyright or
53
+ patent infringement, then any licenses granted to You under this Agreement for that Model or Derivative Model will terminate as of
54
+ the date such litigation is filed. NVIDIA may update this Agreement to comply with legal and regulatory requirements at any time
55
+ and You agree to either comply with any updated license or cease Your copying, use, and distribution of the Model and any
56
+ Derivative Model.
57
+ 2.2.
58
+ License Grant. The rights granted herein are explicitly conditioned on Your full compliance with the terms of this
59
+ Agreement. Subject to the terms and conditions of this Agreement, NVIDIA hereby grants to You a perpetual, worldwide, nonexclusive, no-charge, royalty-free, revocable (as stated in Section 2.1) license to publicly perform, publicly display, reproduce, use,
60
+ create derivative works of, make, have made, sell, offer for sale, distribute (through multiple tiers of distribution) and import the
61
+ Model.
62
+ 2.3.
63
+ AI Ethics. NVIDIA is committed to safety, trust and transparency in AI development. NVIDIA encourages You to (a) ensure
64
+ that the product or service You develop, use, offer as a service or distributes meets the legal and ethical requirements of the
65
+ relevant industry or use case, (b) take reasonable measures to address unintended bias and to mitigate harm to others, including
66
+ underrepresented or vulnerable groups, and (c) inform users of the nature and limitations of the product or service. NVIDIA
67
+ expressly prohibits the use of its products or services for any purpose in violation of applicable law or regulation, including but not
68
+ limited to (a) illegal surveillance, (b) illegal collection or processing of biometric information without the consent of the subject
69
+ where required under applicable law, or (c) illegal harassment, abuse, threatening or bullying of individuals or groups of individuals
70
+ or intentionally misleading or deceiving others.
71
+ 2.4.
72
+ NVIDIA owns the Model and any Model Derivatives created by NVIDIA. Subject to NVIDIA’s underlying ownership rights in
73
+ the Model or its Model Derivatives, You are and will be the owner of Your Model Derivatives. NVIDIA claims no ownership rights in
74
+ outputs. You are responsible for outputs and their subsequent uses. Except as expressly granted in this Agreement, (a) NVIDIA
75
+ reserves all rights, interests and remedies in connection with the Model and (b) no other license or right is granted to you by
76
+ implication, estoppel or otherwise.
77
+ 3.
78
+ Redistribution. You may reproduce and distribute copies of the Model or Derivative Models thereof in any medium, with or
79
+ without modifications, provided that You meet the following conditions:
80
+
81
+ 3.1.
82
+ If you distribute the Model, You must give any other recipients of the Model a copy of this Agreement and include the
83
+ following attribution notice within a “Notice” text file with such copies: “Licensed by NVIDIA Corporation under the NVIDIA Open
84
+ Model License”; and
85
+ 3.2.
86
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms
87
+ and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Models as a whole, provided
88
+ Your use, reproduction, and distribution of the Model otherwise complies with the conditions stated in this Agreement.
89
+ 4.
90
+ Trademarks. This Agreement does not grant permission to use the trade names, trademarks, service marks, or product
91
+ names of NVIDIA, except as required for reasonable and customary use in describing the origin of the Model and reproducing the
92
+ content of the “Notice” text file.
93
+ 5.
94
+ Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, NVIDIA provides the Model on an “AS
95
+ IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any
96
+ warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
97
+ solely responsible for determining the appropriateness of using or redistributing the Model, Derivative Models and outputs and
98
+ assume any risks associated with Your exercise of permissions under this Agreement.
99
+ 6.
100
+ Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or
101
+ otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, will NVIDIA be
102
+ liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a
103
+ result of this Agreement or out of the use or inability to use the Model, Derivative Models or outputs (including but not limited to
104
+ damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or
105
+ losses), even if NVIDIA has been advised of the possibility of such damages.
106
+ 7.
107
+ Indemnity. You will indemnify and hold harmless NVIDIA from and against any claim by any third party arising out of or
108
+ related to your use or distribution of the Model, Model Derivatives or outputs.
109
+ 8.
110
+ You.
111
+
112
+ Feedback. NVIDIA appreciates your feedback, and You agree that NVIDIA may use it without restriction or compensation to
113
+
114
+ 9.
115
+ Governing Law. This Agreement will be governed in all respects by the laws of the United States and the laws of the State
116
+ of Delaware, without regard to conflict of laws principles or the United Nations Convention on Contracts for the International Sale of
117
+ Goods. The state and federal courts residing in Santa Clara County, California will have exclusive jurisdiction over any dispute or
118
+ claim arising out of or related to this Agreement, and the parties irrevocably consent to personal jurisdiction and venue in those
119
+ courts; except that, either party may apply for injunctive remedies or an equivalent type of urgent legal relief in any jurisdiction.
120
+ 10.
121
+ Trade and Compliance. You agree to comply with all applicable export, import, trade and economic sanctions laws and
122
+ regulations, as amended, including without limitation U.S. Export Administration Regulations and Office of Foreign Assets Control
123
+ regulations. These laws include restrictions on destinations, end-users and end-use.
124
+ Version Release Date: June 14, 2024
125
+
126
+
README.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: nvidia-open-model-license
4
+ license_link: https://developer.download.nvidia.com/licenses/nvidia-open-model-license-agreement-june-2024.pdf
5
+ base_model: nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16
6
+ tags: [nemotron, multimodal, mamba2, moe, quantized, turboquant, mlx]
7
+ ---
8
+
9
+ # Nemotron-3-Nano-Omni-30B-A3B-Reasoning - TurboQuant MLX 3-bit
10
+
11
+ MLX 3-bit quantization of the **text tower** of `Nemotron-3-Nano-Omni-30B-A3B-Reasoning` (`nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16`)
12
+ with TurboQuant weight method. Apple Silicon native via `mlx-lm`.
13
+
14
+ This variant covers the LLM backbone only. Vision (CRADIO v4-H) + audio (Parakeet-TDT-0.6B-v2)
15
+ encoders are NOT included — MLX-VLM Nemotron-Omni model class is **pending upstream support**
16
+ (no PR observed as of 2026-05-04). For multimodal inference, use the GGUF variants with
17
+ `llama-mtmd-cli` instead.
18
+
19
+ For the matched-KV stack — TurboQuant weights + TurboQuant KV-cache modifier —
20
+ see [`majentik/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-TurboQuant-MLX-3bit-TQ-KV`](https://huggingface.co/majentik/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-TurboQuant-MLX-3bit-TQ-KV).
21
+ For the runtime KV-cache modifier itself, see
22
+ [`majentik/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-TurboQuant`](https://huggingface.co/majentik/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-TurboQuant).
23
+
24
+ ## Modality matrix
25
+
26
+ | Modality | Encoder | Quantization in this variant |
27
+ |---|---|---|
28
+ | Text | LLM backbone (Mamba-2 + Transformer hybrid Sparse MoE) | per the variant suffix |
29
+ | Image | CRADIO v4-H | **BF16** (kept full-precision in every non-GGUF variant; GGUF uses mmproj-F16 split file) |
30
+ | Audio | Parakeet-TDT-0.6B-v2 | **BF16** (same rationale) |
31
+ | Video | Parakeet-TDT-0.6B-v2 + frame sampler | **BF16** (≤ 2 min, 256 frames @ 2 FPS) |
32
+
33
+ NVIDIA's official FP8 / NVFP4 recipe keeps both encoders + the cross-modal
34
+ MLP projectors in BF16 to preserve multimodal accuracy. We follow that
35
+ convention in every quantized variant we ship.
36
+
37
+ ## Runtime quirks
38
+
39
+ ### MLX-LM (text-only)
40
+
41
+ This variant covers the LLM backbone only. Vision + audio encoders
42
+ are NOT included — MLX-VLM Nemotron-Omni model class is
43
+ **pending upstream support** (no PR observed as of 2026-05-04).
44
+
45
+ Use the `mlx_lm.generate` API; `enable_thinking` is a runtime flag
46
+ (see below).
47
+
48
+ ### Reasoning mode
49
+
50
+ `enable_thinking` defaults to `True`. To disable extended reasoning
51
+ (e.g., for latency-sensitive cases), pass `enable_thinking=False`
52
+ to the chat template / generate call. No separate "no-think"
53
+ variant card exists — this is a runtime flag, not a model variant.
audio_model.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Sound/Audio model components for multimodal integration.
16
+
17
+ This module provides the SoundEncoder (wrapping Parakeet from HuggingFace transformers)
18
+ and SoundProjection (MLP to project audio embeddings to LLM hidden size).
19
+
20
+ The Parakeet model in HuggingFace transformers is documented at:
21
+ https://huggingface.co/docs/transformers/en/model_doc/parakeet
22
+ """
23
+
24
+ from typing import Optional
25
+
26
+ import torch
27
+ import torch.nn as nn
28
+
29
+ from transformers import ParakeetEncoder, ParakeetEncoderConfig
30
+ from transformers.utils import logging
31
+
32
+ logger = logging.get_logger(__name__)
33
+
34
+
35
+ class SquaredReLU(nn.Module):
36
+ """Squared ReLU activation function."""
37
+ def forward(self, x):
38
+ return torch.pow(torch.nn.functional.relu(x), 2)
39
+
40
+
41
+ class RMSNorm(nn.Module):
42
+ def __init__(self, hidden_size, eps=1e-5):
43
+ super().__init__()
44
+ self.weight = nn.Parameter(torch.ones(hidden_size))
45
+ self.eps = eps
46
+
47
+ def forward(self, hidden_states):
48
+ input_dtype = hidden_states.dtype
49
+ hidden_states = hidden_states.to(torch.float32)
50
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
51
+ hidden_states = hidden_states * torch.rsqrt(variance + self.eps)
52
+ return (self.weight.to(torch.float32) * hidden_states).to(input_dtype)
53
+
54
+
55
+ class SoundProjection(nn.Module):
56
+ """MLP projection from sound encoder hidden size to LLM hidden size.
57
+
58
+ Architecture: RMSNorm -> linear1 -> SquaredReLU -> linear2
59
+
60
+ This matches the Megatron checkpoint conversion structure:
61
+ - sound_projection.norm.weight
62
+ - sound_projection.linear1.weight
63
+ - sound_projection.linear2.weight
64
+ - sound_projection.linear1.bias (optional)
65
+ - sound_projection.linear2.bias (optional)
66
+ """
67
+
68
+ def __init__(
69
+ self,
70
+ sound_hidden_size: int,
71
+ projection_hidden_size: int,
72
+ llm_hidden_size: int,
73
+ bias: bool = True,
74
+ eps: float = 1e-5,
75
+ ):
76
+ super().__init__()
77
+ self.norm = RMSNorm(sound_hidden_size, eps=eps)
78
+ self.linear1 = nn.Linear(sound_hidden_size, projection_hidden_size, bias=bias)
79
+ self.activation = SquaredReLU()
80
+ self.linear2 = nn.Linear(projection_hidden_size, llm_hidden_size, bias=bias)
81
+
82
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
83
+ """Project sound embeddings to LLM embedding space.
84
+
85
+ Args:
86
+ hidden_states: Sound encoder output [batch, seq_len, sound_hidden_size]
87
+
88
+ Returns:
89
+ Projected embeddings [batch, seq_len, llm_hidden_size]
90
+ """
91
+ hidden_states = self.norm(hidden_states)
92
+ hidden_states = self.linear1(hidden_states)
93
+ hidden_states = self.activation(hidden_states)
94
+ hidden_states = self.linear2(hidden_states)
95
+ return hidden_states
96
+
97
+
98
+ class SoundEncoder(nn.Module):
99
+ """Wrapper around the Parakeet encoder from HuggingFace transformers.
100
+
101
+ The Parakeet model is an ASR model with a Fast Conformer encoder.
102
+ We use only the encoder portion to extract audio embeddings.
103
+
104
+ Checkpoint structure:
105
+ - sound_encoder.encoder.feature_extractor.* -> Feature extraction (mel spectrogram)
106
+ - sound_encoder.encoder.pre_encode.* -> Pre-encoding convolutions
107
+ - sound_encoder.encoder.layers.* -> Conformer layers
108
+
109
+ Reference: https://huggingface.co/docs/transformers/en/model_doc/parakeet
110
+ """
111
+
112
+ def __init__(self, config=None):
113
+ super().__init__()
114
+
115
+ if config is not None:
116
+ # Build from config - handle both dict and config object
117
+ if hasattr(config, '__dict__'):
118
+ # It's a config object, extract relevant params for ParakeetConfig
119
+ config_dict = {
120
+ 'attention_bias': getattr(config, 'attention_bias', False),
121
+ 'hidden_size': getattr(config, 'hidden_size', 1024),
122
+ 'num_attention_heads': getattr(config, 'num_attention_heads', 8),
123
+ 'num_hidden_layers': getattr(config, 'num_hidden_layers', 24),
124
+ 'intermediate_size': getattr(config, 'intermediate_size', 4096),
125
+ 'conv_kernel_size': getattr(config, 'conv_kernel_size', 31),
126
+ 'convolution_bias': getattr(config, 'convolution_bias', False),
127
+ 'feat_in': getattr(config, 'feat_in', 80),
128
+ 'subsampling_factor': getattr(config, 'subsampling_factor', 8),
129
+ 'subsampling_conv_channels': getattr(config, 'subsampling_conv_channels', 256),
130
+ 'subsampling_conv_kernel_size': getattr(config, 'subsampling_conv_kernel_size', 3),
131
+ 'subsampling_conv_stride': getattr(config, 'subsampling_conv_stride', 2),
132
+ 'num_mel_bins': getattr(config, 'num_mel_bins', 128),
133
+ 'scale_input': getattr(config, 'scale_input', False),
134
+ }
135
+ elif isinstance(config, dict):
136
+ config_dict = config
137
+ else:
138
+ config_dict = {}
139
+
140
+ # Create ParakeetConfig with the extracted parameters
141
+ parakeet_config = ParakeetEncoderConfig(**config_dict)
142
+ self.config = parakeet_config
143
+ self.encoder = ParakeetEncoder(parakeet_config)
144
+ else:
145
+ raise ValueError(
146
+ "config must be provided, "
147
+ "and ParakeetEncoder must be available in transformers."
148
+ )
149
+
150
+ def forward(
151
+ self,
152
+ input_features: torch.Tensor,
153
+ attention_mask: Optional[torch.Tensor] = None,
154
+ ) -> torch.Tensor:
155
+ """Encode audio features.
156
+
157
+ Args:
158
+ input_features: Mel spectrogram features [batch, seq_len, feature_dim]
159
+ attention_mask: Optional attention mask [batch, seq_len]
160
+
161
+ Returns:
162
+ Audio embeddings [batch, encoded_seq_len, hidden_size]
163
+ """
164
+ outputs = self.encoder(
165
+ input_features=input_features,
166
+ attention_mask=attention_mask,
167
+ )
168
+ # Return the last hidden state
169
+ return outputs.last_hidden_state
170
+
171
+ @property
172
+ def hidden_size(self) -> int:
173
+ """Return the hidden size of the encoder."""
174
+ return self.config.hidden_size
chat_template.jinja ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% macro render_extra_keys(json_dict, handled_keys) %}
2
+ {%- if json_dict is mapping %}
3
+ {%- for json_key in json_dict if json_key not in handled_keys %}
4
+ {%- if json_dict[json_key] is mapping or (json_dict[json_key] is sequence and json_dict[json_key] is not string) %}
5
+ {{- '\n<' ~ json_key ~ '>' ~ (json_dict[json_key] | tojson | safe) ~ '</' ~ json_key ~ '>' }}
6
+ {%- else %}
7
+ {{- '\n<' ~ json_key ~ '>' ~ (json_dict[json_key] | string) ~ '</' ~ json_key ~ '>' }}
8
+ {%- endif %}
9
+ {%- endfor %}
10
+ {%- endif %}
11
+ {%- endmacro -%}
12
+ {%- set enable_thinking = enable_thinking if enable_thinking is defined else True %}
13
+ {%- set reasoning_budget = reasoning_budget if reasoning_budget is defined else None %}
14
+ {%- set truncate_history_thinking = truncate_history_thinking if truncate_history_thinking is defined else True %}
15
+
16
+ {#- Scan messages for VLM thinking toggles to override enable_thinking -#}
17
+ {%- set toggle = namespace(enable=enable_thinking) %}
18
+ {%- for m in messages %}
19
+ {%- if m['role'] == 'user' or m['role'] == 'system' -%}
20
+ {%- if m['content'] is string -%}
21
+ {%- set c = m['content'] %}
22
+ {%- if '/think' in c.replace('</think>', '') -%}
23
+ {%- set toggle.enable = true -%}
24
+ {%- elif '/no_think' in c -%}
25
+ {%- set toggle.enable = false -%}
26
+ {%- endif -%}
27
+ {%- else -%}
28
+ {%- for part in m['content'] -%}
29
+ {%- if part['type'] == 'text' -%}
30
+ {%- set c = part['text'] %}
31
+ {%- if '/think' in c.replace('</think>', '') -%}
32
+ {%- set toggle.enable = true -%}
33
+ {%- elif '/no_think' in c -%}
34
+ {%- set toggle.enable = false -%}
35
+ {%- endif -%}
36
+ {%- endif -%}
37
+ {%- endfor -%}
38
+ {%- endif -%}
39
+ {%- endif -%}
40
+ {%- endfor -%}
41
+ {#- Prepare message iteration similar to LM template -#}
42
+ {%- set ns = namespace(last_user_idx = -1) %}
43
+ {%- set loop_messages = messages %}
44
+ {%- for m in loop_messages %}
45
+ {%- if m["role"] == "user" %}
46
+ {%- set ns.last_user_idx = loop.index0 %}
47
+ {%- endif %}
48
+ {%- endfor -%}
49
+
50
+ {%- if messages[0]["role"] == "system" %}
51
+ {%- set system_message = messages[0]["content"] %}
52
+ {%- set loop_messages = messages[1:] %}
53
+ {%- else %}
54
+ {%- set system_message = "" %}
55
+ {%- set loop_messages = messages %}
56
+ {%- endif %}
57
+ {%- if not tools is defined %}
58
+ {%- set tools = [] %}
59
+ {%- endif %}
60
+ {#- Recompute last_user_idx relative to loop_messages after handling system -#}
61
+ {%- set ns = namespace(last_user_idx = -1) %}
62
+ {%- for m in loop_messages %}
63
+ {%- if m["role"] == "user" %}
64
+ {%- set ns.last_user_idx = loop.index0 %}
65
+ {%- endif %}
66
+ {%- endfor -%}
67
+ {#- System preamble with LM formatting, sanitize thinking toggles -#}
68
+ {%- if system_message is defined %}
69
+ {%- set sys_content = system_message | string %}
70
+ {%- set sys_content = sys_content.replace('</think>', '<_end_think>').replace('/think', '').replace('/no_think', '').replace('<_end_think>', '</think>') %}
71
+ {{- "<|im_start|>system\n" + sys_content }}
72
+ {%- else %}
73
+ {%- if tools is iterable and tools | length > 0 %}
74
+ {{- "<|im_start|>system\n" }}
75
+ {%- endif %}
76
+ {%- endif %}
77
+ {%- if tools is iterable and tools | length > 0 %}
78
+ {%- if system_message is defined and system_message | length > 0 %}
79
+ {{- "\n\n" }}
80
+ {%- endif %}
81
+ {{- "# Tools\n\nYou have access to the following functions:\n\n" }}
82
+ {{- "<tools>" }}
83
+ {%- for tool in tools %}
84
+ {%- if tool.function is defined %}
85
+ {%- set tool = tool.function %}
86
+ {%- endif %}
87
+ {{- "\n<function>\n<name>" ~ tool.name ~ "</name>" }}
88
+ {%- if tool.description is defined %}
89
+ {{- '\n<description>' ~ (tool.description | trim) ~ '</description>' }}
90
+ {%- endif %}
91
+ {{- '\n<parameters>' }}
92
+ {%- if tool.parameters is defined and tool.parameters is mapping and tool.parameters.properties is defined and tool.parameters.properties is mapping %}
93
+ {%- for param_name, param_fields in tool.parameters.properties|items %}
94
+ {{- '\n<parameter>' }}
95
+ {{- '\n<name>' ~ param_name ~ '</name>' }}
96
+ {%- if param_fields.type is defined %}
97
+ {{- '\n<type>' ~ (param_fields.type | string) ~ '</type>' }}
98
+ {%- endif %}
99
+ {%- if param_fields.description is defined %}
100
+ {{- '\n<description>' ~ (param_fields.description | trim) ~ '</description>' }}
101
+ {%- endif %}
102
+ {%- if param_fields.enum is defined %}
103
+ {{- '\n<enum>' ~ (param_fields.enum | tojson | safe) ~ '</enum>' }}
104
+ {%- endif %}
105
+ {%- set handled_keys = ['name', 'type', 'description', 'enum'] %}
106
+ {{- render_extra_keys(param_fields, handled_keys) }}
107
+ {{- '\n</parameter>' }}
108
+ {%- endfor %}
109
+ {%- endif %}
110
+ {%- set handled_keys = ['type', 'properties', 'required'] %}
111
+ {{- render_extra_keys(tool.parameters, handled_keys) }}
112
+ {%- if tool.parameters is defined and tool.parameters.required is defined %}
113
+ {{- '\n<required>' ~ (tool.parameters.required | tojson | safe) ~ '</required>' }}
114
+ {%- endif %}
115
+ {{- '\n</parameters>' }}
116
+ {%- set handled_keys = ['type', 'name', 'description', 'parameters'] %}
117
+ {{- render_extra_keys(tool, handled_keys) }}
118
+ {{- '\n</function>' }}
119
+ {%- endfor %}
120
+ {{- "\n</tools>" }}
121
+
122
+ {{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
123
+ {%- endif -%}
124
+ {%- if system_message is defined %}
125
+ {{- '<|im_end|>\n' }}
126
+ {%- else %}
127
+ {%- if tools is iterable and tools | length > 0 %}
128
+ {{- '<|im_end|>\n' }}
129
+ {%- endif %}
130
+ {%- endif -%}
131
+ {#- Iterate conversation -#}
132
+ {%- for message in loop_messages %}
133
+ {%- if message.role == "assistant" %}
134
+ {#- Use LM assistant handling -#}
135
+ {%- if message.reasoning_content is defined and message.reasoning_content is string and message.reasoning_content | trim | length > 0 %}
136
+ {%- set content = "<think>\n" ~ message.reasoning_content ~ "\n</think>\n" ~ (message.content | default('', true)) %}
137
+ {%- else %}
138
+ {%- set content = message.content | default('', true) %}
139
+ {%- if content is string -%}
140
+ {%- if '<think>' not in content and '</think>' not in content -%}
141
+ {%- set content = "<think></think>" ~ content -%}
142
+ {%- endif -%}
143
+ {%- else -%}
144
+ {%- set content = content -%}
145
+ {%- endif -%}
146
+ {%- endif %}
147
+ {%- if message.tool_calls is defined and message.tool_calls is iterable and message.tool_calls | length > 0 %}
148
+ {{- '<|im_start|>assistant\n' }}
149
+ {%- set include_content = not (truncate_history_thinking and loop.index0 < ns.last_user_idx) %}
150
+ {%- if content is string and content | trim | length > 0 %}
151
+ {%- if include_content %}
152
+ {{- (content | trim) ~ '\n' -}}
153
+ {%- else %}
154
+ {%- set c = (content | string) %}
155
+ {%- if '</think>' in c %}
156
+ {%- set c = c.split('</think>')[-1] %}
157
+ {%- elif '<think>' in c %}
158
+ {%- set c = c.split('<think>')[0] %}
159
+ {%- endif %}
160
+ {%- set c = "<think></think>" ~ c | trim %}
161
+ {%- if c | length > 0 %}
162
+ {{- c ~ '\n' -}}
163
+ {%- endif %}
164
+ {%- endif %}
165
+ {%- else %}
166
+ {{- "<think></think>" -}}
167
+ {%- endif %}
168
+ {%- for tool_call in message.tool_calls %}
169
+ {%- if tool_call.function is defined %}
170
+ {%- set tool_call = tool_call.function %}
171
+ {%- endif %}
172
+ {{- '<tool_call>\n<function=' ~ tool_call.name ~ '>\n' -}}
173
+ {%- if tool_call.arguments is defined %}
174
+ {%- for args_name, args_value in tool_call.arguments|items %}
175
+ {{- '<parameter=' ~ args_name ~ '>\n' -}}
176
+ {%- set args_value = args_value | tojson | safe if args_value is mapping or (args_value is sequence and args_value is not string) else args_value | string %}
177
+ {{- args_value ~ '\n</parameter>\n' -}}
178
+ {%- endfor %}
179
+ {%- endif %}
180
+ {{- '</function>\n</tool_call>\n' -}}
181
+ {%- endfor %}
182
+ {{- '<|im_end|>\n' }}
183
+ {%- else %}
184
+ {%- if not (truncate_history_thinking and loop.index0 < ns.last_user_idx) %}
185
+ {{- '<|im_start|>assistant\n' ~ (content | default('', true) | string | trim) ~ '<|im_end|>\n' }}
186
+ {%- else %}
187
+ {%- set c = (content | default('', true) | string) %}
188
+ {%- if '<think>' in c and '</think>' in c %}
189
+ {%- set c = "<think></think>" ~ c.split('</think>')[-1] %}
190
+ {%- endif %}
191
+ {%- set c = c | trim %}
192
+ {%- if c | length > 0 %}
193
+ {{- '<|im_start|>assistant\n' ~ c ~ '<|im_end|>\n' }}
194
+ {%- else %}
195
+ {{- '<|im_start|>assistant\n<|im_end|>\n' }}
196
+ {%- endif %}
197
+ {%- endif %}
198
+ {%- endif %}
199
+ {%- elif message.role == "user" or message.role == "system" %}
200
+ {{- '<|im_start|>' + message.role + '\n' }}
201
+ {#- Build VLM multimodal content when content is a sequence -#}
202
+ {%- if message.content is string -%}
203
+ {%- set content = (message.content | string) %}
204
+ {%- else -%}
205
+ {%- set text_ns = namespace(val='') -%}
206
+ {%- set mm_content = '' -%}
207
+ {%- set counters = namespace(images=0, videos=0, audios=0) -%}
208
+ {%- for part in message['content'] -%}
209
+ {%- if part['type'] == 'image' or part['type'] == 'image_url' -%}
210
+ {%- set counters.images = counters.images + 1 -%}
211
+ {%- elif part['type'] == 'video' or part['type'] == 'video_url' -%}
212
+ {%- set counters.videos = counters.videos + 1 -%}
213
+ {%- elif part['type'] == 'audio' or part['type'] == 'audio_url' -%}
214
+ {%- set counters.audios = counters.audios + 1 -%}
215
+ {%- elif part['type'] == 'text' -%}
216
+ {%- set text_ns.val = text_ns.val + part['text'] -%}
217
+ {%- endif -%}
218
+ {%- endfor -%}
219
+ {%- if '<image>' in text_ns.val -%}
220
+ {%- set counters.images = 0 -%}
221
+ {%- endif -%}
222
+ {%- if '<video>' in text_ns.val -%}
223
+ {%- set counters.videos = 0 -%}
224
+ {%- endif -%}
225
+ {%- if '<so_embedding>' in text_ns.val -%}
226
+ {%- set counters.audios = 0 -%}
227
+ {%- endif -%}
228
+ {%- if counters.images > 1 -%}
229
+ {%- set image_tags = namespace(tags=[]) -%}
230
+ {%- for i in range(counters.images) -%}
231
+ {%- set image_tags.tags = image_tags.tags + ['<image ' + (i + 1)|string + '><image>'] -%}
232
+ {%- endfor -%}
233
+ {%- set mm_content = ' '.join(image_tags.tags) + '\n' -%}
234
+ {%- elif counters.images == 1 -%}
235
+ {%- set mm_content = '<image>\n' -%}
236
+ {%- endif -%}
237
+ {%- set mm_content = mm_content + '<video>\n' * counters.videos -%}
238
+ {%- set mm_content = mm_content + '<so_embedding>\n' * counters.audios -%}
239
+ {%- set content = mm_content + text_ns.val.lstrip('\n') -%}
240
+ {%- endif -%}
241
+ {#- Sanitize thinking toggle directives from user/system content -#}
242
+ {%- set content = content.replace('</think>', '<_end_think>').replace('/think', '').replace('/no_think', '').replace('<_end_think>', '</think>') -%}
243
+ {%- set content = content | trim -%}
244
+ {%- if message.role == "user" and loop.index0 == ns.last_user_idx and reasoning_budget is not none -%}
245
+ {{- content + '\n\n{thinking token budget: ' + (reasoning_budget | string) + '}' -}}
246
+ {%- else -%}
247
+ {{- content -}}
248
+ {%- endif -%}
249
+ {{- '<|im_end|>\n' }}
250
+ {%- elif message.role == "tool" %}
251
+ {%- if loop.previtem and loop.previtem.role != "tool" %}
252
+ {{- '<|im_start|>user\n' }}
253
+ {%- endif %}
254
+ {{- '<tool_response>\n' }}
255
+ {{- message.content }}
256
+ {{- '\n</tool_response>\n' }}
257
+ {%- if not loop.last and loop.nextitem.role != "tool" %}
258
+ {{- '<|im_end|>\n' }}
259
+ {%- elif loop.last %}
260
+ {{- '<|im_end|>\n' }}
261
+ {%- endif %}
262
+ {%- else %}
263
+ {{- '<|im_start|>' + message.role + '\n' + message.content + '<|im_end|>\n' }}
264
+ {%- endif %}
265
+ {%- endfor -%}
266
+ {#- Generation prompt using computed thinking toggle -#}
267
+ {%- if add_generation_prompt %}
268
+ {%- if toggle.enable %}
269
+ {{- '<|im_start|>assistant\n<think>\n' }}
270
+ {%- else %}
271
+ {{- '<|im_start|>assistant\n<think></think>' }}
272
+ {%- endif %}
273
+ {%- endif %}
config.json ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "NemotronH_Nano_Omni_Reasoning_V3"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration.NemotronH_Nano_Omni_Reasoning_V3_Config",
7
+ "AutoModel": "modeling.NemotronH_Nano_Omni_Reasoning_V3",
8
+ "AutoModelForCausalLM": "modeling.NemotronH_Nano_Omni_Reasoning_V3"
9
+ },
10
+ "max_sequence_length": 131072,
11
+ "downsample_ratio": 0.5,
12
+ "force_image_size": 512,
13
+ "patch_size": 16,
14
+ "use_thumbnail": true,
15
+ "eos_token_id": 11,
16
+ "model_type": "NemotronH_Nano_Omni_Reasoning_V3",
17
+ "ps_version": "v2",
18
+ "template": "n5h_5p5_nanov2",
19
+ "torch_dtype": "bfloat16",
20
+ "image_tag_type": "internvl",
21
+ "img_context_token_id": 18,
22
+ "video_context_token_id": 131081,
23
+ "img_context_token": "<image>",
24
+ "video_context_token": "<video>",
25
+ "img_start_token": "<img>",
26
+ "img_end_token": "</img>",
27
+ "vit_hidden_size": 1280,
28
+ "projector_hidden_size": 20480,
29
+ "norm_mean": [
30
+ 0.48145466,
31
+ 0.4578275,
32
+ 0.40821073
33
+ ],
34
+ "norm_std": [
35
+ 0.26862954,
36
+ 0.26130258,
37
+ 0.27577711
38
+ ],
39
+ "video_pruning_rate": 0.7,
40
+ "sound_context_token_id": 27,
41
+ "sound_context_token": "<so_embedding>",
42
+ "sound_config": {
43
+ "model_type": "parakeet",
44
+ "hidden_size": 1024,
45
+ "num_attention_heads": 8,
46
+ "num_hidden_layers": 24,
47
+ "intermediate_size": 4096,
48
+ "conv_kernel_size": 9,
49
+ "convolution_bias": false,
50
+ "subsampling_conv_channels": 256,
51
+ "subsampling_conv_kernel_size": 3,
52
+ "subsampling_conv_stride": 2,
53
+ "subsampling_factor": 8,
54
+ "num_mel_bins": 128,
55
+ "projection_hidden_size": 4096,
56
+ "projection_bias": false,
57
+ "sampling_rate": 16000
58
+ },
59
+ "llm_config": {
60
+ "architectures": [
61
+ "NemotronHForCausalLM"
62
+ ],
63
+ "auto_map": {
64
+ "AutoConfig": "configuration_nemotron_h.NemotronHConfig",
65
+ "AutoModelForCausalLM": "modeling_nemotron_h.NemotronHForCausalLM"
66
+ },
67
+ "model_type": "nemotron_h",
68
+ "bos_token_id": 1,
69
+ "chunk_size": 128,
70
+ "conv_kernel": 4,
71
+ "expand": 2,
72
+ "eos_token_id": 11,
73
+ "pad_token_id": 0,
74
+ "torch_dtype": "bfloat16",
75
+ "transformers_version": "4.55.4",
76
+ "attention_bias": false,
77
+ "attention_dropout": 0.0,
78
+ "head_dim": 128,
79
+ "hidden_dropout": 0.0,
80
+ "hidden_size": 2688,
81
+ "hybrid_override_pattern": "MEMEM*EMEMEM*EMEMEM*EMEMEM*EMEMEM*EMEMEMEM*EMEMEMEME",
82
+ "initializer_range": 0.02,
83
+ "intermediate_size": 1856,
84
+ "layer_norm_epsilon": 1e-05,
85
+ "mamba_head_dim": 64,
86
+ "mamba_hidden_act": "silu",
87
+ "mamba_num_heads": 64,
88
+ "mamba_proj_bias": false,
89
+ "max_position_embeddings": 262144,
90
+ "mlp_bias": false,
91
+ "mlp_hidden_act": "relu2",
92
+ "moe_intermediate_size": 1856,
93
+ "moe_shared_expert_intermediate_size": 3712,
94
+ "n_group": 1,
95
+ "n_groups": 8,
96
+ "n_routed_experts": 128,
97
+ "n_shared_experts": 1,
98
+ "norm_eps": 1e-05,
99
+ "norm_topk_prob": true,
100
+ "num_attention_heads": 32,
101
+ "num_experts_per_tok": 6,
102
+ "num_hidden_layers": 52,
103
+ "num_key_value_heads": 2,
104
+ "num_logits_to_keep": 1,
105
+ "partial_rotary_factor": 1.0,
106
+ "rescale_prenorm_residual": true,
107
+ "residual_in_fp32": false,
108
+ "rope_theta": 10000,
109
+ "routed_scaling_factor": 2.5,
110
+ "sliding_window": null,
111
+ "ssm_state_size": 128,
112
+ "tie_word_embeddings": false,
113
+ "time_step_floor": 0.0001,
114
+ "time_step_limit": [
115
+ 0.0,
116
+ 1e+30
117
+ ],
118
+ "time_step_max": 0.1,
119
+ "time_step_min": 0.001,
120
+ "topk_group": 1,
121
+ "use_bias": false,
122
+ "use_cache": true,
123
+ "use_conv_bias": true,
124
+ "use_mamba_kernels": true,
125
+ "vocab_size": 131072
126
+ },
127
+ "vision_config": {
128
+ "auto_map": {
129
+ "AutoConfig": "nvidia/C-RADIOv2-H--hf_model.RADIOConfig",
130
+ "AutoModel": "nvidia/C-RADIOv2-H--hf_model.RADIOModel"
131
+ },
132
+ "adaptor_configs": {},
133
+ "adaptor_names": null,
134
+ "architectures": [
135
+ "RADIOModel"
136
+ ],
137
+ "args": {
138
+ "aa": null,
139
+ "amp": true,
140
+ "amp_dtype": "bfloat16",
141
+ "amp_impl": "native",
142
+ "aug_repeats": 0,
143
+ "aug_splits": 0,
144
+ "bn_eps": null,
145
+ "bn_momentum": null,
146
+ "cache_dir": null,
147
+ "channels_last": false,
148
+ "checkpoint_hist": 10,
149
+ "chk_keep_forever": 100,
150
+ "class_map": "",
151
+ "clip_grad": null,
152
+ "clip_mode": "norm",
153
+ "cls_token_per_teacher": true,
154
+ "coco_annotations_file": "/datasets/coco2017-adlsa/annotations/captions_val2017.json",
155
+ "coco_image_dir": "/datasets/coco2017-adlsa/val2017",
156
+ "color_jitter": 0.4,
157
+ "cooldown_epochs": 0,
158
+ "cpe_max_size": 2048,
159
+ "crd_loss": false,
160
+ "crd_loss_weight": 0.8,
161
+ "crop_pct": null,
162
+ "cutmix": 0.0,
163
+ "cutmix_minmax": null,
164
+ "dataset_download": false,
165
+ "debug_full_knn": false,
166
+ "decay_epochs": 90,
167
+ "decay_milestones": [
168
+ 90,
169
+ 180,
170
+ 270
171
+ ],
172
+ "decay_rate": 0.1,
173
+ "depchain": true,
174
+ "dist_bn": "reduce",
175
+ "dist_norm_weight": 0.0,
176
+ "distributed": true,
177
+ "drop": 0.0,
178
+ "drop_block": null,
179
+ "drop_connect": null,
180
+ "drop_path": null,
181
+ "dtype": "bfloat16",
182
+ "epoch_repeats": 0.0,
183
+ "eval": false,
184
+ "eval_metric": "knn_top1",
185
+ "eval_teacher": false,
186
+ "eval_teacher_only": false,
187
+ "eval_throughput": false,
188
+ "fast_norm": false,
189
+ "fd_loss_fn": "MSE",
190
+ "feature_normalization": "SHIP_NORM",
191
+ "feature_summarizer": "cls_token",
192
+ "feature_upscale_factor": null,
193
+ "force_new_wandb_id": false,
194
+ "force_spectral_reparam": true,
195
+ "freeze_bn": false,
196
+ "fsdp": false,
197
+ "fuser": "",
198
+ "gp": null,
199
+ "grad_accum_steps": 1,
200
+ "grad_checkpointing": false,
201
+ "head_init_bias": null,
202
+ "head_init_scale": null,
203
+ "head_warmup": 5,
204
+ "head_weight_decay": 0.001,
205
+ "hflip": 0.5,
206
+ "img_size": null,
207
+ "in_chans": null,
208
+ "initial_checkpoint": null,
209
+ "input_size": null,
210
+ "interpolation": "",
211
+ "layer_decay": null,
212
+ "local_rank": 0,
213
+ "log_interval": 50,
214
+ "log_mlflow": false,
215
+ "log_wandb": true,
216
+ "loss_auto_balance": false,
217
+ "lr_base": 0.1,
218
+ "lr_base_scale": "",
219
+ "lr_base_size": 256,
220
+ "lr_cycle_decay": 0.5,
221
+ "lr_cycle_limit": 1,
222
+ "lr_cycle_mul": 1.0,
223
+ "lr_k_decay": 1.0,
224
+ "lr_noise": null,
225
+ "lr_noise_pct": 0.67,
226
+ "lr_noise_std": 1.0,
227
+ "mean": null,
228
+ "mesa": false,
229
+ "min_lr": 0,
230
+ "mixup": 0.0,
231
+ "mixup_mode": "batch",
232
+ "mixup_off_epoch": 0,
233
+ "mixup_prob": 1.0,
234
+ "mixup_switch_prob": 0.5,
235
+ "mlp_hidden_size": 1520,
236
+ "mlp_num_inner": 3,
237
+ "mlp_version": "v2",
238
+ "model": "vit_huge_patch16_224",
239
+ "model_kwargs": {},
240
+ "model_norm": false,
241
+ "momentum": 0.9,
242
+ "no_aug": false,
243
+ "no_ddp_bb": true,
244
+ "no_prefetcher": false,
245
+ "no_resume_opt": false,
246
+ "num_classes": null,
247
+ "opt_betas": null,
248
+ "opt_eps": null,
249
+ "patience_epochs": 10,
250
+ "pin_mem": false,
251
+ "prefetcher": true,
252
+ "pretrained": false,
253
+ "rank": 0,
254
+ "ratio": [
255
+ 0.75,
256
+ 1.3333333333333333
257
+ ],
258
+ "recount": 1,
259
+ "recovery_interval": 0,
260
+ "register_multiple": 10,
261
+ "remode": "pixel",
262
+ "reprob": 0.0,
263
+ "reset_loss_state": false,
264
+ "resplit": false,
265
+ "save_images": false,
266
+ "scale": [
267
+ 0.5,
268
+ 1.0
269
+ ],
270
+ "sched": "cosine",
271
+ "seed": 42,
272
+ "smoothing": 0.1,
273
+ "spectral_heads": false,
274
+ "spectral_reparam": false,
275
+ "split_bn": false,
276
+ "start_epoch": null,
277
+ "std": null,
278
+ "stream_teachers": true,
279
+ "sync_bn": false,
280
+ "synchronize_step": false,
281
+ "teachers": [
282
+ {
283
+ "fd_normalize": false,
284
+ "feature_distillation": true,
285
+ "input_size": 378,
286
+ "model": "ViT-H-14-378-quickgelu",
287
+ "name": "clip",
288
+ "pretrained": "dfn5b",
289
+ "type": "open_clip",
290
+ "use_summary": true
291
+ },
292
+ {
293
+ "fd_normalize": false,
294
+ "feature_distillation": true,
295
+ "input_size": 378,
296
+ "model": "ViT-SO400M-14-SigLIP-384",
297
+ "name": "siglip",
298
+ "pretrained": "webli",
299
+ "type": "open_clip",
300
+ "use_summary": true
301
+ },
302
+ {
303
+ "fd_normalize": false,
304
+ "feature_distillation": true,
305
+ "input_size": 378,
306
+ "model": "dinov2_vitg14_reg",
307
+ "name": "dino_v2",
308
+ "type": "dino_v2",
309
+ "use_summary": true
310
+ },
311
+ {
312
+ "fd_normalize": false,
313
+ "feature_distillation": true,
314
+ "input_size": 1024,
315
+ "model": "vit-h",
316
+ "name": "sam",
317
+ "type": "sam",
318
+ "use_summary": false
319
+ }
320
+ ],
321
+ "torchcompile": null,
322
+ "torchscript": false,
323
+ "train_interpolation": "random",
324
+ "train_split": "train",
325
+ "tta": 0,
326
+ "use_coco": false,
327
+ "use_multi_epochs_loader": false,
328
+ "val_ema_only": false,
329
+ "val_split": "val",
330
+ "vflip": 0.0,
331
+ "vitdet_version": 1,
332
+ "wandb_entity": "",
333
+ "wandb_job_type": "",
334
+ "wandb_name": "",
335
+ "wandb_project": "",
336
+ "warmup_lr": 1e-05,
337
+ "warmup_prefix": false,
338
+ "worker_seeding": "all",
339
+ "workers": 8,
340
+ "world_size": 256,
341
+ "min_num_patches": 1024,
342
+ "max_num_patches": 13312
343
+ },
344
+ "feature_normalizer_config": null,
345
+ "inter_feature_normalizer_config": null,
346
+ "max_resolution": 2048,
347
+ "patch_size": 16,
348
+ "preferred_resolution": [
349
+ 768,
350
+ 768
351
+ ],
352
+ "torch_dtype": "bfloat16",
353
+ "version": "radio_v2.5-h",
354
+ "vitdet_window_size": null,
355
+ "min_num_patches": 1024,
356
+ "max_num_patches": 13312,
357
+ "video_target_num_patches": 1024,
358
+ "video_maintain_aspect_ratio": true,
359
+ "video_temporal_patch_size": 2,
360
+ "video_prompt_version": 2,
361
+ "separate_video_embedder": true
362
+ },
363
+ "quantization": {
364
+ "group_size": 64,
365
+ "bits": 3
366
+ },
367
+ "quantization_config": {
368
+ "group_size": 64,
369
+ "bits": 3
370
+ }
371
+ }
configuration.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from transformers.configuration_utils import PretrainedConfig
15
+ from transformers.utils import logging
16
+ from .configuration_nemotron_h import NemotronHConfig
17
+ from .configuration_radio import RADIOConfig
18
+
19
+ logger = logging.get_logger(__name__)
20
+
21
+
22
+ class SoundConfig(PretrainedConfig):
23
+ """Configuration for the sound/audio model (Parakeet encoder + projection)."""
24
+ model_type = "parakeet"
25
+
26
+ def __init__(
27
+ self,
28
+ # Parakeet encoder config
29
+ hidden_size: int = 1024,
30
+ num_attention_heads: int = 8,
31
+ num_hidden_layers: int = 24,
32
+ intermediate_size: int = 4096,
33
+ conv_kernel_size: int = 31,
34
+ feat_in: int = 80, # Mel features
35
+ subsampling_factor: int = 8,
36
+ # Projection config
37
+ projection_hidden_size: int = 20480,
38
+ projection_bias: bool = True,
39
+ # Audio processing
40
+ sampling_rate: int = 16000,
41
+ **kwargs,
42
+ ):
43
+ super().__init__(**kwargs)
44
+ self.hidden_size = hidden_size
45
+ self.num_attention_heads = num_attention_heads
46
+ self.num_hidden_layers = num_hidden_layers
47
+ self.intermediate_size = intermediate_size
48
+ self.conv_kernel_size = conv_kernel_size
49
+ self.feat_in = feat_in
50
+ self.subsampling_factor = subsampling_factor
51
+ self.projection_hidden_size = projection_hidden_size
52
+ self.projection_bias = projection_bias
53
+ self.sampling_rate = sampling_rate
54
+
55
+
56
+ class NemotronH_Nano_Omni_Reasoning_V3_Config(PretrainedConfig):
57
+ model_type = 'NemotronH_Nano_Omni_Reasoning_V3'
58
+ is_composition = True
59
+
60
+ def __init__(
61
+ self,
62
+ vision_config=None,
63
+ llm_config=None,
64
+ sound_config=None,
65
+ force_image_size=None,
66
+ downsample_ratio=0.5,
67
+ template=None,
68
+ ps_version='v1',
69
+ image_tag_type="internvl",
70
+ projector_hidden_size=4096,
71
+ vit_hidden_size=1280,
72
+ attn_implementation="flash_attention_2",
73
+ video_pruning_rate: float = 0.0,
74
+ video_temporal_patch_size: int = 2,
75
+ # Sound/audio settings
76
+ sound_context_token_id: int = None,
77
+ sound_context_token: str = "<audio>",
78
+ **kwargs
79
+ ):
80
+ super().__init__(**kwargs)
81
+
82
+ if vision_config is not None:
83
+ self.vision_config = RADIOConfig(**vision_config)
84
+ else:
85
+ self.vision_config = RADIOConfig()
86
+
87
+ # Handle both cases: when loading from JSON (llm_config is dict) and when called internally by transformers (llm_config is None)
88
+ if llm_config is not None:
89
+ self.llm_config = NemotronHConfig(**llm_config)
90
+ else:
91
+ self.llm_config = NemotronHConfig()
92
+
93
+ # Sound/audio model configuration
94
+ if sound_config is not None:
95
+ self.sound_config = SoundConfig(**sound_config)
96
+ else:
97
+ self.sound_config = None # Sound model is optional
98
+
99
+ # Assign configuration values
100
+ self.force_image_size = force_image_size
101
+ self.downsample_ratio = downsample_ratio
102
+ self.template = template # TODO move out of here and into the tokenizer
103
+ self.ps_version = ps_version # Pixel shuffle version
104
+ self.image_tag_type = image_tag_type # TODO: into the tokenizer too?
105
+ self.projector_hidden_size = projector_hidden_size
106
+ self.vit_hidden_size = vit_hidden_size
107
+ self.video_pruning_rate = video_pruning_rate
108
+ self.video_temporal_patch_size = video_temporal_patch_size
109
+
110
+ # Sound/audio token settings
111
+ self.sound_context_token_id = sound_context_token_id
112
+ self.sound_context_token = sound_context_token
113
+
114
+ self._attn_implementation = attn_implementation
115
+ self.vision_config.use_flash_attn = self._attn_implementation is not None and "flash_attention" in self._attn_implementation
116
+ self.llm_config._attn_implementation = self._attn_implementation
117
+
118
+ # vLLM's `NemotronH_Nano_VL_V2` implementation reads the language-model sub-config as
119
+ # `config.text_config`. Our HF config stores it as `config.llm_config`; expose an alias so the
120
+ # same config object loads under both loaders without having to duplicate the dict on disk.
121
+ @property
122
+ def text_config(self):
123
+ return self.llm_config
configuration_nemotron_h.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2024 AI21 Labs Ltd. and the HuggingFace Inc. team. All rights reserved.
3
+ # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """NemotronH model configuration"""
17
+
18
+ import re
19
+
20
+ from transformers.configuration_utils import PretrainedConfig
21
+ from transformers.utils import logging
22
+
23
+
24
+ logger = logging.get_logger(__name__)
25
+
26
+
27
+ class NemotronHConfig(PretrainedConfig):
28
+ r"""
29
+ This is the configuration class to store the configuration of a [`NemotronHModel`]. It is used to instantiate a
30
+ NemotronH model according to the specified arguments, defining the model architecture. Instantiating a configuration
31
+ with the defaults will yield a similar configuration to that of the NemotronH-v0.1 model.
32
+
33
+ [todo](todo)
34
+
35
+ Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
36
+ documentation from [`PretrainedConfig`] for more information.
37
+
38
+
39
+ Args:
40
+ vocab_size (`int`, *optional*, defaults to 131072):
41
+ Vocabulary size of the NemotronH model. Defines the number of different tokens that can be represented by the
42
+ `inputs_ids` passed when calling [`NemotronHModel`]
43
+ tie_word_embeddings (`bool`, *optional*, defaults to `False`):
44
+ Whether the model's input and output word embeddings should be tied. Note that this is only relevant if the
45
+ model has a output word embedding layer.
46
+ hidden_size (`int`, *optional*, defaults to 4096):
47
+ Dimension of the hidden representations.
48
+ intermediate_size (`int`, *optional*, defaults to 21504):
49
+ Dimension of the MLP representations.
50
+ num_hidden_layers (`int`, *optional*, defaults to 52):
51
+ Number of hidden layers in the Transformer encoder.
52
+ hybrid_override_pattern (`str`, *optional*, defaults to `"M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M-"`):
53
+ The pattern of the hybrid model. The pattern is a string of characters where each character represents M: Mamba2, *: Attention, -: MLP
54
+ num_attention_heads (`int`, *optional*, defaults to 32):
55
+ Number of attention heads for each attention layer in the Transformer encoder.
56
+ head_dim (`int`, *optional*, defaults to 128):
57
+ Dimension of each attention head.
58
+ num_key_value_heads (`int`, *optional*, defaults to 8):
59
+ This is the number of key_value heads that should be used to implement Grouped Query Attention. If
60
+ `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
61
+ `num_key_value_heads=1` the model will use Multi Query Attention (MQA) otherwise GQA is used.
62
+ mlp_hidden_act (`str`, *optional*, defaults to "relu2"):
63
+ The non-linear activation function in the MLP layers.
64
+ attention_bias (`bool`, *optional*, defaults to `False`):
65
+ Whether to use bias in attention layers.
66
+ mlp_bias (`bool`, *optional*, defaults to `False`):
67
+ Whether to use bias in MLP layers.
68
+ use_bias (`bool`, *optional*, defaults to `False`):
69
+ Whether to use bias in the model.
70
+ initializer_range (`float`, *optional*, defaults to 0.02):
71
+ The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
72
+ layer_norm_epsilon (`float`, *optional*, defaults to 1e-5):
73
+ The epsilon used by the layer normalization layers.
74
+ residual_in_fp32 (`bool`, *optional*, defaults to `False`):
75
+ Whether or not residuals should be in `float32`. If set to `False` residuals will keep the same `dtype` as the rest of the model.
76
+ use_cache (`bool`, *optional*, defaults to `True`):
77
+ Whether or not the model should return the last key/values attentions (not used by all models). Only
78
+ relevant if `config.is_decoder=True`.
79
+ num_logits_to_keep (`int` or `None`, *optional*, defaults to 1):
80
+ Number of prompt logits to calculate during generation. If `None`, all logits will be calculated. If an
81
+ integer value, only last `num_logits_to_keep` logits will be calculated.
82
+ pad_token_id (`int`, *optional*, defaults to 0):
83
+ The id of the padding token.
84
+ bos_token_id (`int`, *optional*, defaults to 1):
85
+ The id of the "beginning-of-sequence" token.
86
+ eos_token_id (`int`, *optional*, defaults to 2):
87
+ The id of the "end-of-sequence" token.
88
+ sliding_window (`int`, *optional*, defaults to None):
89
+ Sliding window attention window size.
90
+ max_position_embeddings (`int`, *optional*, defaults to 4096):
91
+ The maximum sequence length that this model might ever be used with.
92
+ attention_dropout (`float`, *optional*, defaults to 0.0):
93
+ The dropout ratio for the attention probabilities.
94
+ hidden_dropout (`float`, *optional*, defaults to 0.0):
95
+ The dropout ratio for the hidden states.
96
+ use_mamba_kernels (`bool`, *optional*, defaults to `True`):
97
+ Flag indicating whether or not to use the fast mamba kernels. These are available only if `mamba-ssm` and
98
+ `causal-conv1d` are installed, and the mamba modules are running on a CUDA device.
99
+ ssm_state_size (`int`, *optional*, defaults to 128):
100
+ The dimension of the mamba state space latents.
101
+ mamba_num_heads (`int`, *optional*, defaults to 128):
102
+ Number of heads in Mamba layers.
103
+ mamba_n_groups (`int`, *optional*, defaults to 8):
104
+ Number of groups in Mamba layers.
105
+ mamba_head_dim (`int`, *optional*, defaults to 64):
106
+ Dimension of each Mamba head.
107
+ mamba_d_conv (`int`, *optional*, defaults to 4):
108
+ The size of the mamba convolution kernel.
109
+ mamba_expand (`int`, *optional*, defaults to 2):
110
+ Expanding factor used to determine the mamba intermediate size.
111
+ mamba_hidden_act (`str`, *optional*, defaults to "silu"):
112
+ The non-linear activation function in the Mamba layers.
113
+ mamba_dt_min (`float`, *optional*, defaults to 0.001):
114
+ Minimum value for the time step in Mamba.
115
+ mamba_dt_max (`float`, *optional*, defaults to 0.1):
116
+ Maximum value for the time step in Mamba.
117
+ mamba_dt_limit (`tuple`, *optional*, defaults to (0.0, float("inf"))):
118
+ Limits for the time step in Mamba.
119
+ mamba_dt_init_floor (`float`, *optional*, defaults to 1e-4):
120
+ Floor value for time step initialization in Mamba.
121
+ mamba_conv_bias (`bool`, *optional*, defaults to `True`):
122
+ Whether to use bias in the convolution layer of the mamba mixer block.
123
+ mamba_proj_bias (`bool`, *optional*, defaults to `False`):
124
+ Whether to use bias in the input and output projections of the mamba mixer block.
125
+ mamba_chunk_size (`int`, *optional*, defaults to 256):
126
+ Size of chunks for Mamba processing.
127
+ rescale_prenorm_residual (`bool`, *optional*, defaults to `True`):
128
+ Whether to rescale the pre-normalization residual connections.
129
+ """
130
+
131
+ model_type = "nemotron_h"
132
+ keys_to_ignore_at_inference = ["past_key_values"]
133
+
134
+ def __init__(
135
+ self,
136
+ vocab_size=131072,
137
+ tie_word_embeddings=False,
138
+ hidden_size=4096,
139
+ intermediate_size=21504,
140
+ num_hidden_layers=52,
141
+ hybrid_override_pattern="M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M*-M-M-M-M-M-",
142
+ num_attention_heads=32,
143
+ head_dim=128,
144
+ num_key_value_heads=8, # nemo: num_query_groups
145
+ mlp_hidden_act="relu2",
146
+ attention_bias=False,
147
+ mlp_bias=False,
148
+ use_bias=False,
149
+ initializer_range=0.02, # nemo: init_method_std
150
+ layer_norm_epsilon=1e-5, # nemo: layernorm_epsilon
151
+ residual_in_fp32=False, # Megatron Core default value
152
+ use_cache=True,
153
+ num_logits_to_keep=1,
154
+ pad_token_id=0,
155
+ bos_token_id=1,
156
+ eos_token_id=2,
157
+ sliding_window=None,
158
+ max_position_embeddings=4096,
159
+ attention_dropout=0.0,
160
+ hidden_dropout=0.0, # * ADDED
161
+ use_mamba_kernels=True,
162
+ ssm_state_size=128, # mamba_state_size
163
+ mamba_num_heads=128,
164
+ mamba_n_groups=8, # nemo: mamba_ssm_ngroups = num_heads
165
+ mamba_head_dim=64,
166
+ mamba_d_conv=4,
167
+ mamba_expand=2,
168
+ mamba_hidden_act="silu",
169
+ mamba_dt_min=0.001,
170
+ mamba_dt_max=0.1,
171
+ mamba_dt_limit=(0.0, float("inf")),
172
+ mamba_dt_init_floor=1e-4,
173
+ mamba_conv_bias=True,
174
+ mamba_proj_bias=False,
175
+ mamba_chunk_size=128,
176
+ rescale_prenorm_residual=True,
177
+ n_routed_experts=8,
178
+ n_shared_experts=1,
179
+ moe_intermediate_size=7688,
180
+ moe_shared_expert_intermediate_size=7688,
181
+ num_experts_per_tok=2,
182
+ routed_scaling_factor=1.0,
183
+ n_group=1,
184
+ topk_group=1,
185
+ norm_topk_prob=True,
186
+ **kwargs,
187
+ ):
188
+ self.vocab_size = vocab_size
189
+ self.tie_word_embeddings = tie_word_embeddings
190
+ self.hidden_size = hidden_size
191
+ self.intermediate_size = intermediate_size
192
+ self.num_hidden_layers = num_hidden_layers
193
+ self.hybrid_override_pattern = hybrid_override_pattern
194
+ self.num_attention_heads = num_attention_heads
195
+ self.head_dim = head_dim
196
+ self.sliding_window = sliding_window
197
+ self.max_position_embeddings = max_position_embeddings
198
+ self.attention_dropout = attention_dropout
199
+ self.hidden_dropout = hidden_dropout
200
+
201
+ # Validate hybrid_override_pattern
202
+ # M: Mamba2, *: Attention, -: MLP, E: MoE
203
+ assert len(self.hybrid_override_pattern) == self.num_hidden_layers, "hybrid_override_pattern must have the same length as num_hidden_layers"
204
+ assert re.match(r"^[*\-ME]+$", self.hybrid_override_pattern), "hybrid_override_pattern must only contain characters 'M', '*', '-', or 'E'"
205
+
206
+ # for backward compatibility
207
+ if num_key_value_heads is None:
208
+ num_key_value_heads = num_attention_heads
209
+
210
+ self.num_key_value_heads = num_key_value_heads
211
+ self.mlp_hidden_act = mlp_hidden_act
212
+ self.attention_bias = attention_bias
213
+ self.mlp_bias = mlp_bias
214
+ self.use_bias = use_bias
215
+ self.initializer_range = initializer_range
216
+ self.layer_norm_epsilon = layer_norm_epsilon
217
+ self.residual_in_fp32 = residual_in_fp32
218
+
219
+ self.use_cache = use_cache
220
+ self.num_logits_to_keep = num_logits_to_keep
221
+
222
+ self.use_mamba_kernels = use_mamba_kernels
223
+ self.n_groups = mamba_n_groups
224
+ self.mamba_head_dim = mamba_head_dim
225
+ self.ssm_state_size = ssm_state_size
226
+ self.mamba_num_heads = mamba_num_heads
227
+ self.conv_kernel = mamba_d_conv
228
+ self.expand = mamba_expand
229
+ self.mamba_hidden_act = mamba_hidden_act
230
+ self.time_step_min = mamba_dt_min
231
+ self.time_step_max = mamba_dt_max
232
+ self.time_step_limit = mamba_dt_limit
233
+ self.time_step_floor = mamba_dt_init_floor
234
+ self.use_conv_bias = mamba_conv_bias
235
+ self.mamba_proj_bias = mamba_proj_bias
236
+ self.chunk_size = mamba_chunk_size
237
+ self.rescale_prenorm_residual = rescale_prenorm_residual
238
+ self.n_routed_experts = n_routed_experts
239
+ self.n_shared_experts = n_shared_experts
240
+ self.moe_intermediate_size = moe_intermediate_size
241
+ self.moe_shared_expert_intermediate_size = moe_shared_expert_intermediate_size
242
+ self.num_experts_per_tok = num_experts_per_tok
243
+ self.routed_scaling_factor = routed_scaling_factor
244
+ self.n_group = n_group
245
+ self.topk_group = topk_group
246
+ self.norm_topk_prob = norm_topk_prob
247
+
248
+ # Derived per-layer block type list. Transformers 5.6+ looks this up as `layer_types` on the
249
+ # config to pick the correct cache structure (linear attention vs full attention). MLP (stateless)
250
+ # layers are tagged as "moe" here only because the transformers cache validator rejects "mlp";
251
+ # from the cache's point of view, both MLP and MoE layers need no kv cache (they become
252
+ # LinearAttentionLayer with zero state).
253
+ self.layer_types = [
254
+ "mamba" if self.hybrid_override_pattern[i] == "M" else
255
+ "attention" if self.hybrid_override_pattern[i] == "*" else
256
+ "moe" if self.hybrid_override_pattern[i] == "-" else "moe"
257
+ for i in range(self.num_hidden_layers)
258
+ ]
259
+ # Per-layer semantic labels used by the modeling code (includes "mlp", which the
260
+ # transformers `layer_types` validator would reject — that's why `layer_types` above maps
261
+ # "-" → "moe" for cache purposes, while this attribute keeps the true label for the block
262
+ # dispatch in NemotronHBlock).
263
+ self.layers_block_type = [
264
+ "mamba" if self.hybrid_override_pattern[i] == "M" else
265
+ "attention" if self.hybrid_override_pattern[i] == "*" else
266
+ "mlp" if self.hybrid_override_pattern[i] == "-" else "moe"
267
+ for i in range(self.num_hidden_layers)
268
+ ]
269
+
270
+ super().__init__(
271
+ pad_token_id=pad_token_id,
272
+ bos_token_id=bos_token_id,
273
+ eos_token_id=eos_token_id,
274
+ tie_word_embeddings=tie_word_embeddings,
275
+ **kwargs,
276
+ )
configuration_radio.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # NVIDIA CORPORATION and its licensors retain all intellectual property
4
+ # and proprietary rights in and to this software, related documentation
5
+ # and any modifications thereto. Any use, reproduction, disclosure or
6
+ # distribution of this software and related documentation without an express
7
+ # license agreement from NVIDIA CORPORATION is strictly prohibited.
8
+
9
+ from dataclasses import dataclass
10
+ from typing import Optional, NamedTuple, Union, List, Dict
11
+
12
+ from transformers import PretrainedConfig
13
+
14
+
15
+ class Resolution(NamedTuple):
16
+ height: int
17
+ width: int
18
+
19
+
20
+ @dataclass
21
+ class RadioResource:
22
+ url: str
23
+ patch_size: int
24
+ max_resolution: int
25
+ preferred_resolution: Resolution
26
+ vitdet_num_windowed: Optional[int] = None
27
+ vitdet_num_global: Optional[int] = None
28
+
29
+
30
+ RESOURCE_MAP = {
31
+ # RADIOv2.5
32
+ "radio_v2.5-b": RadioResource(
33
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio-v2.5-b_half.pth.tar?download=true",
34
+ patch_size=16,
35
+ max_resolution=2048,
36
+ preferred_resolution=(768, 768),
37
+ vitdet_num_global=4,
38
+ ),
39
+ "radio_v2.5-l": RadioResource(
40
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio-v2.5-l_half.pth.tar?download=true",
41
+ patch_size=16,
42
+ max_resolution=2048,
43
+ preferred_resolution=(768, 768),
44
+ vitdet_num_global=4,
45
+ ),
46
+ "radio_v2.5-h": RadioResource(
47
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-h.pth.tar?download=true",
48
+ patch_size=16,
49
+ max_resolution=2048,
50
+ preferred_resolution=(768, 768),
51
+ vitdet_num_global=4,
52
+ ),
53
+ "radio_v2.5-h-norm": RadioResource(
54
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-h-norm.pth.tar?download=true",
55
+ patch_size=16,
56
+ max_resolution=2048,
57
+ preferred_resolution=(768, 768),
58
+ vitdet_num_global=4,
59
+ ),
60
+ "radio_v2.5-g": RadioResource(
61
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.5-g.pth.tar?download=true",
62
+ patch_size=14,
63
+ max_resolution=1792,
64
+ preferred_resolution=(896, 896),
65
+ vitdet_num_global=8,
66
+ ),
67
+ # RADIO
68
+ "radio_v2.1": RadioResource(
69
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.1_bf16.pth.tar?download=true",
70
+ patch_size=16,
71
+ max_resolution=2048,
72
+ preferred_resolution=Resolution(432, 432),
73
+ vitdet_num_windowed=5,
74
+ ),
75
+ "radio_v2": RadioResource(
76
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v2.pth.tar?download=true",
77
+ patch_size=16,
78
+ max_resolution=2048,
79
+ preferred_resolution=Resolution(432, 432),
80
+ vitdet_num_windowed=5,
81
+ ),
82
+ "radio_v1": RadioResource(
83
+ "https://huggingface.co/nvidia/RADIO/resolve/main/radio_v1.pth.tar?download=true",
84
+ patch_size=14,
85
+ max_resolution=1050,
86
+ preferred_resolution=Resolution(378, 378),
87
+ ),
88
+ # E-RADIO
89
+ "e-radio_v2": RadioResource(
90
+ "https://huggingface.co/nvidia/RADIO/resolve/main/eradio_v2.pth.tar?download=true",
91
+ patch_size=16,
92
+ max_resolution=2048,
93
+ preferred_resolution=Resolution(512, 512),
94
+ ),
95
+ # C-RADIO
96
+ "c-radio_v2.5-g": RadioResource(
97
+ "https://huggingface.co/nvidia/C-RADIOv2-g/resolve/main/c-radio_v2-g_half.pth.tar",
98
+ patch_size=16,
99
+ max_resolution=2048,
100
+ preferred_resolution=(768, 768),
101
+ vitdet_num_global=8,
102
+ ),
103
+ "c-radio_v3-l": RadioResource(
104
+ # NOTE: Currently, this model cannot be loaded via TorchHub. Instead, use the transformers API at https://huggingface.co/nvidia/C-RADIOv3-L
105
+ # and accept the license terms.
106
+ "https://huggingface.co/nvidia/C-RADIOv3-L/resolve/main/c-radio-v3_l_half.pth.tar?download=true",
107
+ patch_size=16,
108
+ max_resolution=2048,
109
+ preferred_resolution=Resolution(512, 512),
110
+ ),
111
+ }
112
+
113
+ DEFAULT_VERSION = "radio_v2.5-h"
114
+
115
+
116
+ class RADIOConfig(PretrainedConfig):
117
+ """Pretrained Hugging Face configuration for RADIO models."""
118
+
119
+ def __init__(
120
+ self,
121
+ args: Optional[dict] = None,
122
+ version: Optional[str] = DEFAULT_VERSION,
123
+ patch_size: Optional[int] = None,
124
+ max_resolution: Optional[int] = None,
125
+ preferred_resolution: Optional[Resolution] = None,
126
+ adaptor_names: Union[str, List[str]] = None,
127
+ adaptor_configs: Dict[str, Dict[str, int]] = None,
128
+ vitdet_window_size: Optional[int] = None,
129
+ feature_normalizer_config: Optional[dict] = None,
130
+ inter_feature_normalizer_config: Optional[dict] = None,
131
+ **kwargs,
132
+ ):
133
+ self.args = args
134
+ for field in ["dtype", "amp_dtype"]:
135
+ if self.args is not None and field in self.args:
136
+ # Convert to a string in order to make it serializable.
137
+ # For example for torch.float32 we will store "float32",
138
+ # for "bfloat16" we will store "bfloat16".
139
+ self.args[field] = str(args[field]).split(".")[-1]
140
+ self.version = version
141
+ resource = RESOURCE_MAP[version]
142
+ self.patch_size = patch_size or resource.patch_size
143
+ self.max_resolution = max_resolution or resource.max_resolution
144
+ self.preferred_resolution = (
145
+ preferred_resolution or resource.preferred_resolution
146
+ )
147
+ self.adaptor_names = adaptor_names
148
+ self.adaptor_configs = adaptor_configs
149
+ self.vitdet_window_size = vitdet_window_size
150
+ self.feature_normalizer_config = feature_normalizer_config
151
+ self.inter_feature_normalizer_config = inter_feature_normalizer_config
152
+ super().__init__(**kwargs)
evs.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import Tuple
3
+
4
+ class EfficientVideoSampling:
5
+ @staticmethod
6
+ def compute_retention_mask(
7
+ *,
8
+ video_embeds: torch.FloatTensor,
9
+ thw: torch.LongTensor,
10
+ spatial_merge_size: int,
11
+ q: float,
12
+ ):
13
+ """
14
+ Computes the retention mask for video embeddings based on the grid dimensions.
15
+
16
+ Args:
17
+ video_embeds (`torch.FloatTensor` of shape `(T * H * W, hidden_size)`):
18
+ The video embeddings to compute the retention mask for.
19
+ thw (`torch.LongTensor` of shape `(3)`):
20
+ The temporal, height and width of feature shape of each video in LLM.
21
+ spatial_merge_size (`int`): The spatial merge size of the video embeddings.
22
+ If embeddings will be downsampled *later*, this should be the downsampling factor.
23
+ q: (`float`): Pruning rate factor, indicating number of tokens to prune (remove)
24
+
25
+ Returns:
26
+ `torch.Tensor`: The retention mask for the video embeddings (T * H * W).
27
+ 1 for tokens to keep, 0 for tokens to prune.
28
+ """
29
+ T, H, W = thw
30
+
31
+ # video_embeds = einops.rearrange(
32
+ # video_embeds,
33
+ # "(T H W) C -> T H W C",
34
+ # T=T,
35
+ # H=H // spatial_merge_size,
36
+ # W=W // spatial_merge_size,
37
+ # )
38
+ # Use reshape instead of einops to avoid graph breaks
39
+ video_embeds = video_embeds.reshape(
40
+ T, H // spatial_merge_size, W // spatial_merge_size, video_embeds.size(-1)
41
+ )
42
+
43
+ # Core EVS
44
+ similarity = torch.nn.functional.cosine_similarity(
45
+ video_embeds[1:, ...], video_embeds[:-1, ...], dim=-1
46
+ )
47
+ dissimilarity = 1 - similarity
48
+
49
+ # Always ensure we include all tokens from the first frame
50
+ dissimilarity = torch.cat(
51
+ [255 * torch.ones_like(video_embeds[:1, :, :, 0]), dissimilarity], dim=0
52
+ )
53
+ dissimilarity_flat = dissimilarity.view(-1)
54
+
55
+ min_num_tokens = (H // spatial_merge_size) * (W // spatial_merge_size) # a single frame
56
+ evs_num_tokens = int(T * min_num_tokens * (1 - q))
57
+ num_tokens_to_keep = max(min_num_tokens, evs_num_tokens)
58
+
59
+ order = torch.argsort(dissimilarity_flat,
60
+ dim=-1,
61
+ descending=True,
62
+ stable=True)
63
+ topk_indices = order[:num_tokens_to_keep]
64
+
65
+ retention_mask = torch.zeros_like(dissimilarity_flat, dtype=torch.bool)
66
+ retention_mask[topk_indices] = True
67
+ retention_mask = retention_mask.reshape(dissimilarity.size())
68
+
69
+ # print(
70
+ # f"Computed retention mask of shape {retention_mask.shape=} with sparsity {retention_mask.float().mean().item():.4f} for {q=}",
71
+ # )
72
+ mask = retention_mask.view(-1) # "T H W -> (T H W)"
73
+ return mask
generation_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 1,
4
+ "eos_token_id": [2, 11],
5
+ "pad_token_id": 0,
6
+ "do_sample": true,
7
+ "temperature": 0.6,
8
+ "top_p": 0.95,
9
+ "max_new_tokens": 16384,
10
+ "reasoning_budget": 16384,
11
+ "reasoning_grace": 512,
12
+ "repetition_penalty": 1.0,
13
+ "transformers_version": "4.55.4"
14
+ }
image_processing.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from typing import List, Optional, Union
3
+
4
+ from PIL import Image
5
+ import torch
6
+ from transformers.image_processing_base import BatchFeature
7
+ from transformers.image_processing_utils_fast import BaseImageProcessorFast
8
+ from transformers.image_utils import make_list_of_images, get_image_type, ImageInput, ImageType
9
+ from transformers.utils import TensorType
10
+ import torchvision.transforms as T
11
+
12
+
13
+ class NemotronH_Nano_Omni_Reasoning_V3ImageProcessor(BaseImageProcessorFast):
14
+ """
15
+ Dynamic-resolution image processor for the V3 omni model.
16
+
17
+ Each image is resized to a single tile whose patch-grid `(h_patches, w_patches)` is chosen to
18
+ land between `min_num_patches` and `max_num_patches` (on a 16×16-pixel grid), respecting
19
+ aspect ratio. This matches the algorithm in vLLM's `DynamicResolutionImageTiler`
20
+ (`vllm/model_executor/models/nano_nemotron_vl.py`) so HF and vLLM inference see identical pixel
21
+ inputs.
22
+ """
23
+
24
+ model_input_names = ["pixel_values"]
25
+
26
+ def __init__(
27
+ self,
28
+ norm_mean=None,
29
+ norm_std=None,
30
+ patch_size=16,
31
+ downsample_ratio=0.5,
32
+ min_num_patches=1024,
33
+ max_num_patches=13312,
34
+ max_model_len=16384,
35
+ video_target_num_patches=1024,
36
+ video_maintain_aspect_ratio=True,
37
+ **kwargs,
38
+ ):
39
+ super().__init__(**kwargs)
40
+ self.norm_mean = norm_mean
41
+ self.norm_std = norm_std
42
+ self.patch_size = patch_size
43
+ self.downsample_ratio = downsample_ratio
44
+ # Integer reduction factor for pixel_shuffle (downsample_ratio = 0.5 → factor 2).
45
+ self._downsample_factor = int(round(1.0 / downsample_ratio))
46
+ # Per-image patch-grid bounds (on the pre-pixel-shuffle 16×16 grid).
47
+ self.min_num_patches = min_num_patches
48
+ self.max_num_patches = max_num_patches
49
+ self.max_model_len = max_model_len
50
+ # Video frames use a separate (fixed) target-patch budget with aspect-ratio preserved.
51
+ # Matches vLLM's `_compute_aspect_preserving_size` in `nano_nemotron_vl.py`.
52
+ self.video_target_num_patches = video_target_num_patches
53
+ self.video_maintain_aspect_ratio = video_maintain_aspect_ratio
54
+
55
+ # Keep the PIL image through to `_preprocess` — we need PIL.resize (bicubic) to match vLLM's
56
+ # algorithm exactly; resizing a tensor via `torchvision.transforms.Resize` uses different
57
+ # kernels and breaks bit-exact agreement.
58
+ def _process_image(self, image: ImageInput, **kwargs):
59
+ if get_image_type(image) == ImageType.PIL:
60
+ if image.mode != "RGB":
61
+ image = image.convert("RGB")
62
+ return image
63
+
64
+ # transformers 5.6 renamed this hook from `_process_image` to `process_image`; alias both.
65
+ process_image = _process_image
66
+
67
+ # Toggled by `processing.py` around video calls (the strict `ImagesKwargs` validator won't let
68
+ # us thread a new kwarg down, so we use a flag on the instance instead).
69
+ _is_video_mode: bool = False
70
+
71
+ def _preprocess(
72
+ self,
73
+ images,
74
+ return_tensors: Optional[Union[str, TensorType]] = None,
75
+ **kwargs,
76
+ ) -> BatchFeature:
77
+ """Port of vLLM's `DynamicResolutionImageTiler._images_to_pixel_values_lst`.
78
+
79
+ When `self._is_video_mode=True` (flipped by `processing.py` before the video call), each
80
+ input is resized using the **video** target-size rule (`video_target_num_patches`,
81
+ aspect-ratio preserved) instead of the image dynamic-res rule. This matches vLLM's split
82
+ between `video_to_pixel_values` (video path) and `DynamicResolutionImageTiler` (image
83
+ path).
84
+ """
85
+ is_video = self._is_video_mode
86
+ images = make_list_of_images(images)
87
+
88
+ target_sizes = []
89
+ if is_video:
90
+ for img in images:
91
+ target_w_patches, target_h_patches = self._compute_target_patches_video(img)
92
+ target_sizes.append((target_w_patches, target_h_patches))
93
+ else:
94
+ # Image path: per-image budget bounded by [min_num_patches, max_num_patches], with a
95
+ # global cap derived from `max_model_len` × pixel-shuffle factor².
96
+ num_tokens_available = self.max_model_len - 4 # match vLLM's reserve
97
+ budget = num_tokens_available * (self._downsample_factor ** 2)
98
+ budget = max(budget, self.min_num_patches * len(images))
99
+ max_budget = self.max_num_patches if (self.max_num_patches and self.max_num_patches > 0) else float("inf")
100
+ per_image_budget = [max(min(budget, max_budget), self.min_num_patches) for _ in images]
101
+ # Single-pass — vLLM has an iterative scale-down for the batch, but it rarely binds in
102
+ # single-image / small-batch inference.
103
+ for img, tokens_for_media in zip(images, per_image_budget):
104
+ target_w_patches, target_h_patches = self._compute_target_patches(img, tokens_for_media)
105
+ target_sizes.append((target_w_patches, target_h_patches))
106
+
107
+ import numpy as np
108
+ norm_mean = torch.tensor(self.norm_mean).view(1, 3, 1, 1)
109
+ norm_std = torch.tensor(self.norm_std).view(1, 3, 1, 1)
110
+
111
+ pixel_values_list = []
112
+ num_tokens_per_image = []
113
+ imgs_sizes = []
114
+ for img, (wp, hp) in zip(images, target_sizes):
115
+ target_w = wp * self.patch_size
116
+ target_h = hp * self.patch_size
117
+ # Use torch's antialiased bicubic interpolation to match vLLM's
118
+ # `_bicubic_resize_and_normalize` (`torch.nn.functional.interpolate`, `antialias=True`).
119
+ # PIL's bicubic uses a different kernel (and no antialiasing), producing visibly different
120
+ # pixel values that amplify through the 52-layer ViT / mamba stack and cause HF/vLLM
121
+ # outputs to diverge past the first few tokens.
122
+ arr = np.asarray(img, dtype=np.uint8) # (H, W, 3)
123
+ t = torch.from_numpy(arr).permute(2, 0, 1).unsqueeze(0).to(dtype=torch.float32) # (1, 3, H, W)
124
+ if t.shape[-2] != target_h or t.shape[-1] != target_w:
125
+ t = torch.nn.functional.interpolate(
126
+ t, size=(target_h, target_w), mode="bicubic", align_corners=False, antialias=True
127
+ )
128
+ t = (t / 255.0 - norm_mean) / norm_std
129
+ pixel_values_list.append(t.squeeze(0)) # (3, H, W)
130
+ num_tokens_per_image.append((wp * hp) // (self._downsample_factor ** 2))
131
+ imgs_sizes.append((target_h, target_w))
132
+
133
+ # Stack if all images have the same target size (common for same-aspect-ratio batches);
134
+ # otherwise keep as a list of (3, H_i, W_i) tensors. The outer model's `extract_feature`
135
+ # handles both.
136
+ all_same_shape = all(t.shape == pixel_values_list[0].shape for t in pixel_values_list)
137
+ if all_same_shape:
138
+ pixel_values = torch.stack(pixel_values_list, dim=0)
139
+ else:
140
+ pixel_values = pixel_values_list
141
+
142
+ return BatchFeature(
143
+ data={
144
+ "pixel_values": pixel_values,
145
+ # One tile per image in dynamic mode — `num_tokens` is what the text-side
146
+ # placeholder expansion should use.
147
+ "num_patches": [1] * len(images),
148
+ "num_tokens": num_tokens_per_image,
149
+ "imgs_sizes": imgs_sizes,
150
+ },
151
+ tensor_type=(return_tensors if all_same_shape else None),
152
+ )
153
+
154
+ def _compute_target_patches(self, img: Image.Image, tokens_available: int):
155
+ """Port of `DynamicResolutionImageTiler.process_media` (image-only, no thumbnail)."""
156
+ orig_w, orig_h = img.width, img.height
157
+ # Ceil-ish: `round(x + 0.5)` == `floor(x) + 1` for non-integer x, `x` for integer.
158
+ closest_patch_h = round(orig_h / self.patch_size + 0.5)
159
+ closest_patch_w = round(orig_w / self.patch_size + 0.5)
160
+ patches = closest_patch_h * closest_patch_w
161
+
162
+ # Downscale to fit the token budget.
163
+ factor = min(math.sqrt(tokens_available / patches), 1.0)
164
+ target_h = math.floor(factor * closest_patch_h)
165
+ target_w = math.floor(factor * closest_patch_w)
166
+
167
+ # Scale up if below the per-image minimum.
168
+ if (
169
+ tokens_available > self.min_num_patches
170
+ and target_h * target_w < self.min_num_patches
171
+ ):
172
+ up = math.sqrt(self.min_num_patches / (target_h * target_w))
173
+ target_h = math.ceil(up * target_h)
174
+ target_w = math.ceil(up * target_w)
175
+
176
+ # Round each dim to a multiple of the pixel_shuffle factor so tokens divide evenly.
177
+ divisor = self._downsample_factor
178
+ rem_h = target_h % divisor
179
+ if rem_h:
180
+ inc_h = divisor - rem_h
181
+ if (target_h + inc_h) * target_w <= tokens_available:
182
+ target_h += inc_h
183
+ else:
184
+ target_h = max(divisor, target_h - rem_h)
185
+ rem_w = target_w % divisor
186
+ if rem_w:
187
+ inc_w = divisor - rem_w
188
+ if target_h * (target_w + inc_w) <= tokens_available:
189
+ target_w += inc_w
190
+ else:
191
+ target_w = max(divisor, target_w - rem_w)
192
+
193
+ return target_w, target_h
194
+
195
+ def _compute_target_patches_video(self, img: Image.Image):
196
+ """Port of vLLM's `_compute_aspect_preserving_size` for video frames.
197
+
198
+ Each frame is resized to roughly `video_target_num_patches` (default 1024) on the 16×16
199
+ grid, with aspect ratio preserved and dims snapped to a multiple of the pixel_shuffle
200
+ factor. For `maintain_aspect_ratio=False`, it falls back to a square of sqrt(target)
201
+ patches.
202
+ """
203
+ orig_w, orig_h = img.width, img.height
204
+ target = self.video_target_num_patches
205
+ divisor = self._downsample_factor # 2 for pixel_shuffle
206
+ if self.video_maintain_aspect_ratio:
207
+ aspect_wh = orig_w / max(orig_h, 1)
208
+ ph = max(round(math.sqrt(target / aspect_wh)), 1)
209
+ pw = max(round(math.sqrt(target * aspect_wh)), 1)
210
+ if divisor > 1:
211
+ rem_h = ph % divisor
212
+ rem_w = pw % divisor
213
+ ph_up = ph + (divisor - rem_h if rem_h else 0)
214
+ ph_down = ph - rem_h
215
+ pw_up = pw + (divisor - rem_w if rem_w else 0)
216
+ pw_down = pw - rem_w
217
+ # Prefer rounding up when the up-rounded patch count still fits the target;
218
+ # otherwise round down (mirrors vLLM's logic exactly).
219
+ if ph_up * pw_up <= target:
220
+ ph, pw = ph_up, pw_up
221
+ else:
222
+ ph = max(divisor, ph_down)
223
+ pw = max(divisor, pw_down)
224
+ else:
225
+ side = int(math.sqrt(target))
226
+ side = max(divisor, (side // divisor) * divisor)
227
+ ph = pw = side
228
+ return pw, ph
model-00001-of-00004.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ca94492401d9dd573e9db1bf775af6a47c4a74e0f7552e52c8bd2021cc018d3d
3
+ size 5369191530
model-00002-of-00004.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7af0adc06f77169285bbd8bbc6fe02e51e55fbfe5df025626387ac81833bf180
3
+ size 5368030759
model-00003-of-00004.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:345727605f07bc7379365ae5f4dd0119475980c0809101a4858ab7c6c1c93cc1
3
+ size 5369622810
model-00004-of-00004.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:33d65b4a04f1b8a14f280f78d549eed18058a5ab9bcb02cc732ca18fb872a9e9
3
+ size 588120685
model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
preprocessor_config.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "image_processor_type": "NemotronH_Nano_Omni_Reasoning_V3ImageProcessor",
3
+ "auto_map": {
4
+ "AutoImageProcessor": "image_processing.NemotronH_Nano_Omni_Reasoning_V3ImageProcessor",
5
+ "AutoVideoProcessor": "video_processing.NemotronH_Nano_Omni_Reasoning_V3VideoProcessor",
6
+ "AutoProcessor": "processing.NemotronH_Nano_Omni_Reasoning_V3Processor"
7
+ },
8
+ "patch_size": 16,
9
+ "downsample_ratio": 0.5,
10
+ "norm_mean": [0.48145466, 0.4578275, 0.40821073],
11
+ "norm_std": [0.26862954, 0.26130258, 0.27577711],
12
+ "min_num_patches": 1024,
13
+ "max_num_patches": 13312,
14
+ "max_model_len": 16384
15
+ }
special_tokens_map.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": {
3
+ "content": "<s>",
4
+ "lstrip": false,
5
+ "normalized": false,
6
+ "rstrip": false,
7
+ "single_word": false
8
+ },
9
+ "eos_token": {
10
+ "content": "<|im_end|>",
11
+ "lstrip": false,
12
+ "normalized": false,
13
+ "rstrip": false,
14
+ "single_word": false
15
+ },
16
+ "unk_token": {
17
+ "content": "<unk>",
18
+ "lstrip": false,
19
+ "normalized": false,
20
+ "rstrip": false,
21
+ "single_word": false
22
+ }
23
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e5e7dc84d72e8f248321611c3d6dce23407b135f55f8caf5b26119798d12f85f
3
+ size 17077367
tokenizer_config.json ADDED
The diff for this file is too large to render. See raw diff