machiabeli commited on
Commit
74dfecb
·
verified ·
1 Parent(s): c836800

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. README.md +31 -0
  2. chat_template.jinja +112 -0
  3. config.json +0 -0
  4. configuration_deepseek.py +214 -0
  5. configuration_kimi_k25.py +123 -0
  6. generation_config.json +4 -0
  7. kimi_k25_processor.py +165 -0
  8. kimi_k25_vision_processing.py +251 -0
  9. media_utils.py +368 -0
  10. model-00002-of-00180.safetensors +3 -0
  11. model-00007-of-00180.safetensors +3 -0
  12. model-00008-of-00180.safetensors +3 -0
  13. model-00010-of-00180.safetensors +3 -0
  14. model-00022-of-00180.safetensors +3 -0
  15. model-00027-of-00180.safetensors +3 -0
  16. model-00030-of-00180.safetensors +3 -0
  17. model-00035-of-00180.safetensors +3 -0
  18. model-00043-of-00180.safetensors +3 -0
  19. model-00046-of-00180.safetensors +3 -0
  20. model-00049-of-00180.safetensors +3 -0
  21. model-00063-of-00180.safetensors +3 -0
  22. model-00071-of-00180.safetensors +3 -0
  23. model-00074-of-00180.safetensors +3 -0
  24. model-00082-of-00180.safetensors +3 -0
  25. model-00087-of-00180.safetensors +3 -0
  26. model-00088-of-00180.safetensors +3 -0
  27. model-00090-of-00180.safetensors +3 -0
  28. model-00095-of-00180.safetensors +3 -0
  29. model-00103-of-00180.safetensors +3 -0
  30. model-00106-of-00180.safetensors +3 -0
  31. model-00109-of-00180.safetensors +3 -0
  32. model-00111-of-00180.safetensors +3 -0
  33. model-00114-of-00180.safetensors +3 -0
  34. model-00123-of-00180.safetensors +3 -0
  35. model-00126-of-00180.safetensors +3 -0
  36. model-00129-of-00180.safetensors +3 -0
  37. model-00142-of-00180.safetensors +3 -0
  38. model-00147-of-00180.safetensors +3 -0
  39. model-00148-of-00180.safetensors +3 -0
  40. model-00150-of-00180.safetensors +3 -0
  41. model-00155-of-00180.safetensors +3 -0
  42. model-00162-of-00180.safetensors +3 -0
  43. model-00167-of-00180.safetensors +3 -0
  44. model-00168-of-00180.safetensors +3 -0
  45. model.safetensors.index.json +0 -0
  46. modeling_deepseek.py +1815 -0
  47. modeling_kimi_k25.py +1294 -0
  48. tokenization_kimi.py +371 -0
  49. tokenizer_config.json +219 -0
  50. tool_declaration_ts.py +479 -0
README.md ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language: en
3
+ pipeline_tag: text-generation
4
+ tags:
5
+ - mlx
6
+ library_name: mlx
7
+ ---
8
+
9
+ # mlx-community/Kimi-K2.7-Code-4bit
10
+
11
+ ## Use with mlx
12
+
13
+ ```bash
14
+ pip install mlx-lm
15
+ ```
16
+
17
+ ```python
18
+ from mlx_lm import load, generate
19
+
20
+ model, tokenizer = load("mlx-community/Kimi-K2.7-Code-4bit")
21
+
22
+ prompt = "hello"
23
+
24
+ if tokenizer.chat_template is not None:
25
+ messages = [{"role": "user", "content": prompt}]
26
+ prompt = tokenizer.apply_chat_template(
27
+ messages, add_generation_prompt=True, return_dict=False,
28
+ )
29
+
30
+ response = generate(model, tokenizer, prompt=prompt, verbose=True)
31
+ ```
chat_template.jinja ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {%- macro render_content(msg) -%}
2
+ {%- set c = msg.get('content') -%}
3
+ {%- if c is string -%}
4
+ {{ c }}
5
+ {%- elif c is not none -%}
6
+ {% for content in c -%}
7
+ {% if content['type'] == 'image' or content['type'] == 'image_url' -%}
8
+ <|media_begin|>image<|media_content|><|media_pad|><|media_end|>
9
+ {% elif content['type'] == 'video' or content['type']== 'video_url'-%}
10
+ <|kimi_k25_video_placeholder|>
11
+ {% else -%}
12
+ {{ content['text'] }}
13
+ {%- endif -%}
14
+ {%- endfor -%}
15
+ {%- endif -%}
16
+ {%- endmacro -%}
17
+
18
+ {% macro set_roles(message) -%}
19
+ {%- set role_name = message.get('name') or message['role'] -%}
20
+ {%- if message['role'] == 'user' -%}
21
+ <|im_user|>{{role_name}}<|im_middle|>
22
+ {%- elif message['role'] == 'assistant' -%}
23
+ <|im_assistant|>{{role_name}}<|im_middle|>
24
+ {%- else -%}
25
+ <|im_system|>{{role_name}}<|im_middle|>
26
+ {%- endif -%}
27
+ {%- endmacro -%}
28
+
29
+
30
+ {%- macro render_toolcalls(message) -%}
31
+ <|tool_calls_section_begin|>
32
+ {%- for tool_call in message['tool_calls'] -%}
33
+ {%- set formatted_id = tool_call['id'] -%}
34
+ <|tool_call_begin|>{{ formatted_id }}<|tool_call_argument_begin|>{% if tool_call['function']['arguments'] is string %}{{ tool_call['function']['arguments'] }}{% else %}{{ tool_call['function']['arguments'] | tojson }}{% endif %}<|tool_call_end|>
35
+ {%- endfor -%}
36
+ <|tool_calls_section_end|>
37
+ {%- endmacro -%}
38
+
39
+
40
+ {%- set preserve_thinking = preserve_thinking | default(true) -%}
41
+ {# Find last non-tool-call assistant message. If preserve_thinking, keep -1 so hist is empty and all msgs use suffix (retain reasoning). #}
42
+ {%- set ns = namespace(last_non_tool_call_assistant_msg=-1) -%}
43
+ {%- if not preserve_thinking -%}
44
+ {%- for idx in range(messages|length-1, -1, -1) -%}
45
+ {%- if messages[idx]['role'] == 'assistant' and not messages[idx].get('tool_calls') -%}
46
+ {%- set ns.last_non_tool_call_assistant_msg = idx -%}
47
+ {%- break -%}
48
+ {%- endif -%}
49
+ {%- endfor -%}
50
+ {%- endif -%}
51
+
52
+ {# split all messages into history & suffix, reasoning_content in suffix should be reserved.#}
53
+ {%- set hist_msgs = messages[:ns.last_non_tool_call_assistant_msg+1] -%}
54
+ {%- set suffix_msgs = messages[ns.last_non_tool_call_assistant_msg+1:] -%}
55
+
56
+ {%- if tools -%}
57
+ {%- if tools_ts_str -%}
58
+ <|im_system|>tool_declare<|im_middle|>{{ tools_ts_str }}<|im_end|>
59
+ {%- else -%}
60
+ <|im_system|>tool_declare<|im_middle|>{{ tools | tojson(separators=(',', ':')) }}<|im_end|>
61
+ {%- endif -%}
62
+ {%- endif -%}
63
+
64
+
65
+ {%- for message in hist_msgs -%}
66
+ {{set_roles(message)}}
67
+ {%- if message['role'] == 'assistant' -%}
68
+ <think></think>{{render_content(message)}}
69
+ {%- if message.get('tool_calls') -%}
70
+ {{render_toolcalls(message)}}
71
+ {%- endif -%}
72
+ {%- elif message['role'] == 'tool' -%}
73
+ {%- set tool_call_id = message.tool_call_id -%}
74
+ ## Return of {{ tool_call_id }}
75
+ {{render_content(message)}}
76
+ {%- elif message['content'] is not none -%}
77
+ {{render_content(message)}}
78
+ {%- endif -%}
79
+ <|im_end|>
80
+ {%- endfor -%}
81
+
82
+ {%- for message in suffix_msgs -%}
83
+ {{set_roles(message)}}
84
+ {%- if message['role'] == 'assistant' -%}
85
+ {%- if thinking is defined and thinking is false and preserve_thinking is false -%}
86
+ <think></think>{{render_content(message)}}
87
+ {%- else -%}
88
+ {%- set rc = message.get('reasoning', message.get('reasoning_content', '')) -%}
89
+ <think>{{rc}}</think>{{render_content(message)}}
90
+ {%- endif -%}
91
+ {%- if message.get('tool_calls') -%}
92
+ {{render_toolcalls(message)}}
93
+ {%- endif -%}
94
+ {%- elif message['role'] == 'tool' -%}
95
+ {%- set tool_call_id = message.tool_call_id -%}
96
+ ## Return of {{ tool_call_id }}
97
+ {{render_content(message)}}
98
+ {%- elif message['content'] is not none -%}
99
+ {{render_content(message)}}
100
+ {%- endif -%}
101
+ <|im_end|>
102
+ {%- endfor -%}
103
+
104
+
105
+ {%- if add_generation_prompt -%}
106
+ <|im_assistant|>assistant<|im_middle|>
107
+ {%- if thinking is defined and thinking is false -%}
108
+ <think></think>
109
+ {%- else -%}
110
+ <think>
111
+ {%- endif -%}
112
+ {%- endif -%}
config.json ADDED
The diff for this file is too large to render. See raw diff
 
configuration_deepseek.py ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copy from https://huggingface.co/deepseek-ai/DeepSeek-V3/blob/main/configuration_deepseek.py
2
+
3
+ from transformers.configuration_utils import PretrainedConfig
4
+ from transformers.utils import logging
5
+
6
+ logger = logging.get_logger(__name__)
7
+
8
+ DEEPSEEK_PRETRAINED_CONFIG_ARCHIVE_MAP = {}
9
+
10
+
11
+ class DeepseekV3Config(PretrainedConfig):
12
+ r"""
13
+ This is the configuration class to store the configuration of a [`DeepseekV3Model`]. It is used to instantiate an DeepSeek
14
+ model according to the specified arguments, defining the model architecture. Instantiating a configuration with the
15
+ defaults will yield a similar configuration to that of the DeepSeek-V3.
16
+
17
+ Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
18
+ documentation from [`PretrainedConfig`] for more information.
19
+
20
+
21
+ Args:
22
+ vocab_size (`int`, *optional*, defaults to 129280):
23
+ Vocabulary size of the Deep model. Defines the number of different tokens that can be represented by the
24
+ `inputs_ids` passed when calling [`DeepseekV3Model`]
25
+ hidden_size (`int`, *optional*, defaults to 4096):
26
+ Dimension of the hidden representations.
27
+ intermediate_size (`int`, *optional*, defaults to 11008):
28
+ Dimension of the MLP representations.
29
+ moe_intermediate_size (`int`, *optional*, defaults to 1407):
30
+ Dimension of the MoE representations.
31
+ num_hidden_layers (`int`, *optional*, defaults to 32):
32
+ Number of hidden layers in the Transformer decoder.
33
+ num_nextn_predict_layers (`int`, *optional*, defaults to 1):
34
+ Number of nextn predict layers in the DeepSeekV3 Model.
35
+ num_attention_heads (`int`, *optional*, defaults to 32):
36
+ Number of attention heads for each attention layer in the Transformer decoder.
37
+ n_shared_experts (`int`, *optional*, defaults to None):
38
+ Number of shared experts, None means dense model.
39
+ n_routed_experts (`int`, *optional*, defaults to None):
40
+ Number of routed experts, None means dense model.
41
+ routed_scaling_factor (`float`, *optional*, defaults to 1.0):
42
+ Scaling factor or routed experts.
43
+ topk_method (`str`, *optional*, defaults to `gready`):
44
+ Topk method used in routed gate.
45
+ n_group (`int`, *optional*, defaults to None):
46
+ Number of groups for routed experts.
47
+ topk_group (`int`, *optional*, defaults to None):
48
+ Number of selected groups for each token(for each token, ensuring the selected experts is only within `topk_group` groups).
49
+ num_experts_per_tok (`int`, *optional*, defaults to None):
50
+ Number of selected experts, None means dense model.
51
+ moe_layer_freq (`int`, *optional*, defaults to 1):
52
+ The frequency of the MoE layer: one expert layer for every `moe_layer_freq - 1` dense layers.
53
+ first_k_dense_replace (`int`, *optional*, defaults to 0):
54
+ Number of dense layers in shallow layers(embed->dense->dense->...->dense->moe->moe...->lm_head).
55
+ \--k dense layers--/
56
+ norm_topk_prob (`bool`, *optional*, defaults to False):
57
+ Whether to normalize the weights of the routed experts.
58
+ scoring_func (`str`, *optional*, defaults to 'softmax'):
59
+ Method of computing expert weights.
60
+ aux_loss_alpha (`float`, *optional*, defaults to 0.001):
61
+ Auxiliary loss weight coefficient.
62
+ seq_aux = (`bool`, *optional*, defaults to True):
63
+ Whether to compute the auxiliary loss for each individual sample.
64
+ num_key_value_heads (`int`, *optional*):
65
+ This is the number of key_value heads that should be used to implement Grouped Query Attention. If
66
+ `num_key_value_heads=num_attention_heads`, the model will use Multi Head Attention (MHA), if
67
+ `num_key_value_heads=1 the model will use Multi Query Attention (MQA) otherwise GQA is used. When
68
+ converting a multi-head checkpoint to a GQA checkpoint, each group key and value head should be constructed
69
+ by meanpooling all the original heads within that group. For more details checkout [this
70
+ paper](https://arxiv.org/pdf/2305.13245.pdf). If it is not specified, will default to
71
+ `num_attention_heads`.
72
+ hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
73
+ The non-linear activation function (function or string) in the decoder.
74
+ max_position_embeddings (`int`, *optional*, defaults to 2048):
75
+ The maximum sequence length that this model might ever be used with.
76
+ initializer_range (`float`, *optional*, defaults to 0.02):
77
+ The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
78
+ rms_norm_eps (`float`, *optional*, defaults to 1e-06):
79
+ The epsilon used by the rms normalization layers.
80
+ use_cache (`bool`, *optional*, defaults to `True`):
81
+ Whether or not the model should return the last key/values attentions (not used by all models). Only
82
+ relevant if `config.is_decoder=True`.
83
+ pad_token_id (`int`, *optional*):
84
+ Padding token id.
85
+ bos_token_id (`int`, *optional*, defaults to 1):
86
+ Beginning of stream token id.
87
+ eos_token_id (`int`, *optional*, defaults to 2):
88
+ End of stream token id.
89
+ pretraining_tp (`int`, *optional*, defaults to 1):
90
+ Experimental feature. Tensor parallelism rank used during pretraining. Please refer to [this
91
+ document](https://huggingface.co/docs/transformers/parallelism) to understand more about it. This value is
92
+ necessary to ensure exact reproducibility of the pretraining results. Please refer to [this
93
+ issue](https://github.com/pytorch/pytorch/issues/76232).
94
+ tie_word_embeddings (`bool`, *optional*, defaults to `False`):
95
+ Whether to tie weight embeddings
96
+ rope_theta (`float`, *optional*, defaults to 10000.0):
97
+ The base period of the RoPE embeddings.
98
+ rope_scaling (`Dict`, *optional*):
99
+ Dictionary containing the scaling configuration for the RoPE embeddings. Currently supports two scaling
100
+ strategies: linear and dynamic. Their scaling factor must be a float greater than 1. The expected format is
101
+ `{"type": strategy name, "factor": scaling factor}`. When using this flag, don't update
102
+ `max_position_embeddings` to the expected new maximum.
103
+ attention_bias (`bool`, defaults to `False`, *optional*, defaults to `False`):
104
+ Whether to use a bias in the query, key, value and output projection layers during self-attention.
105
+ attention_dropout (`float`, *optional*, defaults to 0.0):
106
+ The dropout ratio for the attention probabilities.
107
+
108
+ ```python
109
+ >>> from transformers import DeepseekV3Model, DeepseekV3Config
110
+
111
+ >>> # Initializing a Deepseek-V3 style configuration
112
+ >>> configuration = DeepseekV3Config()
113
+
114
+ >>> # Accessing the model configuration
115
+ >>> configuration = model.config
116
+ ```"""
117
+
118
+ model_type = "deepseek_v3"
119
+ keys_to_ignore_at_inference = ["past_key_values"]
120
+
121
+ def __init__(
122
+ self,
123
+ vocab_size=129280,
124
+ hidden_size=7168,
125
+ intermediate_size=18432,
126
+ moe_intermediate_size=2048,
127
+ num_hidden_layers=61,
128
+ num_nextn_predict_layers=1,
129
+ num_attention_heads=128,
130
+ num_key_value_heads=128,
131
+ n_shared_experts=1,
132
+ n_routed_experts=256,
133
+ ep_size=1,
134
+ routed_scaling_factor=2.5,
135
+ kv_lora_rank=512,
136
+ q_lora_rank=1536,
137
+ qk_rope_head_dim=64,
138
+ v_head_dim=128,
139
+ qk_nope_head_dim=128,
140
+ topk_method='noaux_tc',
141
+ n_group=8,
142
+ topk_group=4,
143
+ num_experts_per_tok=8,
144
+ moe_layer_freq=1,
145
+ first_k_dense_replace=3,
146
+ norm_topk_prob=True,
147
+ scoring_func='sigmoid',
148
+ aux_loss_alpha=0.001,
149
+ seq_aux=True,
150
+ hidden_act="silu",
151
+ max_position_embeddings=4096,
152
+ initializer_range=0.02,
153
+ rms_norm_eps=1e-6,
154
+ use_cache=True,
155
+ pad_token_id=None,
156
+ bos_token_id=0,
157
+ eos_token_id=1,
158
+ pretraining_tp=1,
159
+ tie_word_embeddings=False,
160
+ rope_theta=10000.0,
161
+ rope_scaling=None,
162
+ attention_bias=False,
163
+ attention_dropout=0.0,
164
+ **kwargs,
165
+ ):
166
+ self.vocab_size = vocab_size
167
+ self.max_position_embeddings = max_position_embeddings
168
+ self.hidden_size = hidden_size
169
+ self.intermediate_size = intermediate_size
170
+ self.moe_intermediate_size = moe_intermediate_size
171
+ self.num_hidden_layers = num_hidden_layers
172
+ self.num_nextn_predict_layers = num_nextn_predict_layers
173
+ self.num_attention_heads = num_attention_heads
174
+ self.n_shared_experts = n_shared_experts
175
+ self.n_routed_experts = n_routed_experts
176
+ self.ep_size = ep_size
177
+ self.routed_scaling_factor = routed_scaling_factor
178
+ self.kv_lora_rank = kv_lora_rank
179
+ self.q_lora_rank = q_lora_rank
180
+ self.qk_rope_head_dim = qk_rope_head_dim
181
+ self.v_head_dim = v_head_dim
182
+ self.qk_nope_head_dim = qk_nope_head_dim
183
+ self.topk_method = topk_method
184
+ self.n_group = n_group
185
+ self.topk_group = topk_group
186
+ self.num_experts_per_tok = num_experts_per_tok
187
+ self.moe_layer_freq = moe_layer_freq
188
+ self.first_k_dense_replace = first_k_dense_replace
189
+ self.norm_topk_prob = norm_topk_prob
190
+ self.scoring_func = scoring_func
191
+ self.aux_loss_alpha = aux_loss_alpha
192
+ self.seq_aux = seq_aux
193
+ # for backward compatibility
194
+ if num_key_value_heads is None:
195
+ num_key_value_heads = num_attention_heads
196
+
197
+ self.num_key_value_heads = num_key_value_heads
198
+ self.hidden_act = hidden_act
199
+ self.initializer_range = initializer_range
200
+ self.rms_norm_eps = rms_norm_eps
201
+ self.pretraining_tp = pretraining_tp
202
+ self.use_cache = use_cache
203
+ self.rope_theta = rope_theta
204
+ self.rope_scaling = rope_scaling
205
+ self.attention_bias = attention_bias
206
+ self.attention_dropout = attention_dropout
207
+
208
+ super().__init__(
209
+ pad_token_id=pad_token_id,
210
+ bos_token_id=bos_token_id,
211
+ eos_token_id=eos_token_id,
212
+ tie_word_embeddings=tie_word_embeddings,
213
+ **kwargs,
214
+ )
configuration_kimi_k25.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers.configuration_utils import PretrainedConfig
2
+
3
+ try:
4
+ from configuration_deepseek import DeepseekV3Config
5
+ except ImportError:
6
+ from .configuration_deepseek import DeepseekV3Config
7
+
8
+
9
+ class KimiK25VisionConfig(PretrainedConfig):
10
+
11
+ def __init__(
12
+ self,
13
+ patch_size: int = 14,
14
+ init_pos_emb_height: int = 64,
15
+ init_pos_emb_width: int = 64,
16
+ init_pos_emb_time: int = 4,
17
+ pos_emb_type: str = 'divided_fixed',
18
+ vt_num_attention_heads: int = 16,
19
+ vt_num_hidden_layers: int = 27,
20
+ vt_hidden_size: int = 1152,
21
+ vt_intermediate_size: int = 4304,
22
+ merge_kernel_size: tuple = (2, 2),
23
+ video_attn_type: str = 'spatial_temporal',
24
+ merge_type: str = 'sd2_tpool',
25
+ _attn_implementation: str = 'flash_attention_2',
26
+ # MM Projector parameters
27
+ mm_projector_type: str = 'patchmerger',
28
+ mm_hidden_size: int | None = None,
29
+ projector_hidden_act: str = "gelu",
30
+ projector_ln_eps: float = 1e-5,
31
+ # Other parameters
32
+ ignore_index: int = -100,
33
+ media_placeholder_token_id: int = 163605,
34
+ pad_token_id: int = 0,
35
+ use_unified_vision_chunk: bool = True,
36
+ video_placeholder="<|kimi_k25_video_placeholder|>",
37
+ text_hidden_size=7168,
38
+ **vision_config_kwargs):
39
+
40
+ self.patch_size = patch_size
41
+ self.init_pos_emb_height = init_pos_emb_height
42
+ self.init_pos_emb_width = init_pos_emb_width
43
+ self.init_pos_emb_time = init_pos_emb_time
44
+ self.pos_emb_type = pos_emb_type
45
+ self.vt_num_attention_heads = vt_num_attention_heads
46
+ self.vt_num_hidden_layers = vt_num_hidden_layers
47
+ self.vt_hidden_size = vt_hidden_size
48
+ self.vt_intermediate_size = vt_intermediate_size
49
+ self.merge_kernel_size = merge_kernel_size
50
+ self.video_attn_type = video_attn_type
51
+ self.merge_type = merge_type
52
+ self._attn_implementation = _attn_implementation
53
+
54
+ # MM Projector config
55
+ self.mm_projector_type = mm_projector_type
56
+ self.mm_hidden_size = mm_hidden_size if mm_hidden_size is not None else vt_hidden_size
57
+ self.projector_hidden_act = projector_hidden_act
58
+ self.projector_ln_eps = projector_ln_eps
59
+ self.text_hidden_size = text_hidden_size
60
+
61
+
62
+ class KimiK25Config(PretrainedConfig):
63
+ """Kimi-K2.5 model configuration.
64
+
65
+ Args:
66
+ text_config (dict | DeepseekV3Config): Configuration for the text model.
67
+
68
+ Vision Tower Parameters (from MoonViT3dConfig):
69
+ patch_size (int): Patch size for vision tower.
70
+ init_pos_emb_height (int): Initial position embedding height.
71
+ init_pos_emb_width (int): Initial position embedding width.
72
+ init_pos_emb_time (int): Initial position embedding time dimension.
73
+ pos_emb_type (str): Type of position embedding.
74
+ vt_num_attention_heads (int): Number of attention heads in vision tower.
75
+ vt_num_hidden_layers (int): Number of hidden layers in vision tower.
76
+ vt_hidden_size (int): Hidden size of vision tower.
77
+ vt_intermediate_size (int): Intermediate size in vision tower FFN.
78
+ merge_kernel_size (tuple): Kernel size for patch merging.
79
+ video_attn_type (str): Type of video attention.
80
+ merge_type (str): Type of merge operation.
81
+ _attn_implementation (str): Attention implementation type.
82
+
83
+ MM Projector Parameters (from MultiModalProjectorConfig):
84
+ mm_projector_type (str): Type of multimodal projector.
85
+ mm_hidden_size (int): Hidden size from vision tower (should match vt_hidden_size).
86
+ projector_hidden_act (str): Activation function for projector.
87
+ projector_ln_eps (float): Layer norm epsilon for projector.
88
+
89
+ Other Parameters:
90
+ ignore_index (int): The ignore index for the loss function.
91
+ media_placeholder_token_id (int): The token ID to use for media placeholders.
92
+ pad_token_id (int): The token ID to use for padding.
93
+ """
94
+
95
+ model_type = "kimi_k25"
96
+
97
+ def __init__(
98
+ self,
99
+ text_config: dict | DeepseekV3Config = None,
100
+ vision_config: dict | KimiK25VisionConfig = None,
101
+ # Other parameters
102
+ ignore_index: int = -100,
103
+ media_placeholder_token_id: int = 163605,
104
+ pad_token_id: int = 0,
105
+ use_unified_vision_chunk: bool = True,
106
+ video_placeholder="<|kimi_k25_video_placeholder|>",
107
+ **kwargs,
108
+ ):
109
+ if isinstance(text_config, dict):
110
+ text_config = DeepseekV3Config(**text_config)
111
+ if isinstance(vision_config, dict):
112
+ vision_config = KimiK25VisionConfig(**vision_config)
113
+ self.text_config = text_config
114
+ self.vision_config = vision_config
115
+ # Other config
116
+ self.ignore_index = ignore_index
117
+ self.media_placeholder_token_id = media_placeholder_token_id
118
+ self.use_unified_vision_chunk = use_unified_vision_chunk
119
+ self.video_placeholder = video_placeholder
120
+ if getattr(self.text_config, "quantization_config", None) is not None:
121
+ self.quantization_config = self.text_config.quantization_config
122
+
123
+ super().__init__(pad_token_id=pad_token_id, **kwargs)
generation_config.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "max_length": 262144,
3
+ "eos_token_id": 163586
4
+ }
kimi_k25_processor.py ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers.feature_extraction_utils import BatchFeature
2
+ from transformers.processing_utils import ProcessorMixin
3
+ from transformers.utils import logging
4
+
5
+ logger = logging.get_logger(__name__)
6
+
7
+
8
+ class KimiK25Processor(ProcessorMixin):
9
+ r"""
10
+ Constructs a KimiK25 processor which wraps a KimiK25 image processor and a tokenizer into a single processor.
11
+
12
+ [`KimiK25Processor`] offers all the functionalities of [`KimiK25ImageProcessor`] and [`TikTokenTokenizer`]. See the
13
+ [`~KimiK25Processor.__call__`] and [`~KimiK25Processor.decode`] for more information.
14
+
15
+ Args:
16
+ image_processor ([`KimiK25ImageProcessor`], *optional*):
17
+ The image processor is a required input.
18
+ tokenizer ([`TikTokenTokenizer`], *optional*):
19
+ The tokenizer is a required input.
20
+ chat_template (`str`, *optional*): A Jinja template which will be used to convert lists of messages
21
+ in a chat into a tokenizable string.
22
+ """
23
+
24
+ attributes = ["image_processor", "tokenizer"]
25
+ valid_kwargs = ["chat_template"]
26
+ image_processor_class = "AutoImageProcessor"
27
+ tokenizer_class = "AutoTokenizer"
28
+
29
+ def __init__(
30
+ self,
31
+ image_processor=None,
32
+ tokenizer=None,
33
+ chat_template=None,
34
+ **kwargs,
35
+ ):
36
+ super().__init__(image_processor,
37
+ tokenizer,
38
+ chat_template=chat_template)
39
+ self.media_processor = image_processor
40
+ # A special temporal placeholder to be replaced by actual video placeholders
41
+ self.video_placeholder = "<|kimi_k25_video_placeholder|>"
42
+
43
+ def update_raw_text(self, text: str, video_prompts: list[str]) -> str:
44
+ # replace video prompt in text with video chunk prompts
45
+ video_count = text.count(self.video_placeholder)
46
+ if video_count == 0:
47
+ return text
48
+ assert video_count == len(video_prompts)
49
+ text_parts = text.split(self.video_placeholder)
50
+ assert len(text_parts) == len(video_prompts) + 1
51
+ text = "".join([
52
+ text_parts[i] + video_prompts[i] for i in range(len(video_prompts))
53
+ ])
54
+ text += text_parts[-1]
55
+ return text
56
+
57
+ def preprocess_medias(self, medias: list[dict]) -> list[dict]:
58
+ updated_medias = []
59
+ video_prompts = []
60
+ for media in medias:
61
+ if media['type'] == 'image':
62
+ updated_medias.append(media)
63
+ elif media['type'] == 'video':
64
+ video_chunks = self.media_processor.split_video_chunks(
65
+ media['video'])
66
+ updated_medias.extend(video_chunks)
67
+ video_prompts.append("".join(
68
+ [vc['prompt'] for vc in video_chunks]))
69
+ else:
70
+ raise ValueError(f"unsupported media type: {media['type']}")
71
+ return updated_medias, video_prompts
72
+
73
+ def __call__(self,
74
+ messages: list[dict] = None,
75
+ medias: list[dict] = None,
76
+ text: str = None,
77
+ return_tensors: str = "pt",
78
+ **kwargs) -> BatchFeature:
79
+ """
80
+ Process multimodal inputs for Kimi-K2.5 model.
81
+
82
+ This processor accepts ordered messages and extracts both media and text in a single pass.
83
+ text will be automatically updated if video input detected in messages
84
+
85
+ Args:
86
+ messages: List of message dicts with 'role' and 'content' fields.
87
+ If provided, medias and text will be extracted automatically.
88
+ medias: Pre-extracted list of media dicts. If None, extracted from messages.
89
+ text: Pre-formatted text string. If None, generated via apply_chat_template.
90
+ return_tensors: Format of returned tensors ('pt', 'np', 'tf'). Default: 'pt'.
91
+ **kwargs: Additional arguments passed to tokenizer.apply_chat_template.
92
+
93
+ Returns:
94
+ BatchFeature with fields: input_ids, attention_mask, pixel_values, grid_thws.
95
+ """
96
+ if messages is None and (medias is None or text is None):
97
+ raise ValueError(
98
+ "Provide either 'messages' or both 'medias' and 'text'")
99
+
100
+ if medias is not None and text is not None:
101
+ updated_medias, video_prompts = self.preprocess_medias(medias)
102
+ preprocessed = self.media_processor.preprocess(
103
+ updated_medias, return_tensors=return_tensors)
104
+ text = self.update_raw_text(text, video_prompts)
105
+ text_inputs = self.tokenizer(text, return_tensors=return_tensors)
106
+ return BatchFeature(data={**text_inputs, **preprocessed.data})
107
+
108
+ if medias is None:
109
+ medias = self._extract_medias_from_messages(messages)
110
+ updated_medias, video_prompts = self.preprocess_medias(medias)
111
+ preprocessed = self.media_processor.preprocess(
112
+ updated_medias, return_tensors=return_tensors)
113
+
114
+ # Generate text if not provided
115
+ if text is None:
116
+ text = self.tokenizer.apply_chat_template(messages, **kwargs)
117
+
118
+ text = self.update_raw_text(text, video_prompts)
119
+
120
+ text_inputs = self.tokenizer(text, return_tensors=return_tensors)
121
+ return BatchFeature(data={**text_inputs, **preprocessed.data})
122
+
123
+ @staticmethod
124
+ def _extract_medias_from_messages(messages: list[dict]) -> list[dict]:
125
+ """
126
+ Extract media items from messages in a single pass.
127
+
128
+ This is an optimized version that processes messages only once.
129
+ Kept as internal method since external callers should use __call__.
130
+ """
131
+ medias = []
132
+ for msg in messages:
133
+ if msg['role'] != 'user' or not msg.get('content'):
134
+ continue
135
+
136
+ for content_part in msg['content']:
137
+ if not isinstance(content_part, dict):
138
+ continue
139
+
140
+ content_type = content_part.get('type')
141
+ if content_type in ['video_url', 'video']:
142
+ medias.append({
143
+ 'type': 'video',
144
+ 'video': content_part['video_url']['url'],
145
+ 'first_frame_timestamp': 0.0
146
+ })
147
+ elif content_type in ['image_url', 'image']:
148
+ medias.append({
149
+ 'type': 'image',
150
+ 'image': content_part['image_url'],
151
+ })
152
+ return medias
153
+
154
+ def apply_chat_template(self, messages, **kwargs):
155
+ return self.tokenizer.apply_chat_template(messages, **kwargs)
156
+
157
+ def batch_decode(self, *args, **kwargs):
158
+ return self.tokenizer.batch_decode(*args, **kwargs)
159
+
160
+ def decode(self, *args, **kwargs):
161
+ return self.tokenizer.decode(*args, **kwargs)
162
+
163
+ @property
164
+ def model_input_names(self):
165
+ return ['input_ids', 'attention_mask', 'pixel_values', 'grid_thws']
kimi_k25_vision_processing.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Image processor class for Kimi-K2.5.
2
+ """
3
+
4
+ import json
5
+ from typing import Any, Dict, Optional, Union
6
+
7
+ import numpy as np
8
+ import torch
9
+ from PIL import Image
10
+ from transformers.image_processing_utils import (BaseImageProcessor,
11
+ BatchFeature)
12
+ from transformers.utils import TensorType
13
+
14
+ from .media_utils import (MediaInput, VideoChunkInput, _to_tensor,
15
+ ensure_media_type, get_video_meta, image_to_np,
16
+ navit_patchify, navit_resize_image,
17
+ navit_resize_video, normalize,
18
+ real_sample_fps_and_max_num_frames, timestamp_as_str)
19
+
20
+ try:
21
+ from mecord import VideoReader
22
+ except ImportError:
23
+ VideoReader = None
24
+
25
+
26
+ def resampling(video_bytes: bytes,
27
+ sample_indices: list[int],
28
+ key_indices=None,
29
+ frame_time_info=None,
30
+ num_threads=4) -> str:
31
+ video = VideoReader(video_bytes,
32
+ num_threads=num_threads,
33
+ frame_time_info=frame_time_info,
34
+ key_indices=key_indices)
35
+ # extract target frames
36
+ frames = video[sample_indices]
37
+ frames = [Image.fromarray(frame) for frame in frames]
38
+ return frames
39
+
40
+
41
+ class KimiK25VisionProcessor(BaseImageProcessor):
42
+ model_type = "kimi_k25"
43
+
44
+ def __init__(
45
+ self,
46
+ media_proc_cfg: dict,
47
+ **kwargs,
48
+ ):
49
+ super().__init__(**kwargs)
50
+ self.media_proc_cfg = media_proc_cfg
51
+ self.num_frames_per_chunk = media_proc_cfg[
52
+ 'temporal_merge_kernel_size']
53
+
54
+ def media_tokens_calculator(self, media: MediaInput):
55
+ media = ensure_media_type(media)
56
+ ret = self.get_resize_config(media)
57
+ return ret['num_tokens']
58
+
59
+ @classmethod
60
+ def make_chunk_prompt(cls, timestamp_text: str) -> str:
61
+ return f"{timestamp_text}<|media_begin|>video<|media_content|><|media_pad|><|media_end|>"
62
+
63
+ def split_video_chunks(self,
64
+ video_url: str | bytes) -> list[list[Image.Image]]:
65
+ # video_url should be base64 str or bytes
66
+ video_spec = get_video_meta(video_url)
67
+ sample_fps = min(self.media_proc_cfg['sample_fps'], video_spec.fps)
68
+ sampled_nframes = max(
69
+ round(video_spec.num_frames * sample_fps / video_spec.fps), 1)
70
+ frame_inds = np.linspace(0, video_spec.num_frames - 1,
71
+ sampled_nframes).round().astype(int)
72
+ frame_inds = frame_inds.tolist()
73
+ sampled_frame_ids = []
74
+ temporal_merge_kernel_size = self.media_proc_cfg[
75
+ "temporal_merge_kernel_size"]
76
+ num_chunks = 0
77
+ chunk_timestamp = []
78
+ for i in range(0, len(frame_inds), temporal_merge_kernel_size):
79
+ sampled_frame_ids.extend(frame_inds[i:i +
80
+ temporal_merge_kernel_size])
81
+ start_time = frame_inds[i] / float(video_spec.fps)
82
+ timestamp_text = timestamp_as_str(
83
+ start_time, self.media_proc_cfg["timestamp_mode"])
84
+ chunk_timestamp.append(timestamp_text)
85
+ num_chunks += 1
86
+
87
+ sampled_frames = resampling(video_url, sampled_frame_ids)
88
+ chunks = []
89
+ for chunk_id in range(num_chunks):
90
+ chunk = sampled_frames[chunk_id *
91
+ temporal_merge_kernel_size:(chunk_id + 1) *
92
+ temporal_merge_kernel_size]
93
+ chunks.append(
94
+ VideoChunkInput(type="video_chunk",
95
+ video_chunk=chunk,
96
+ prompt=self.make_chunk_prompt(
97
+ chunk_timestamp[chunk_id])))
98
+ return chunks
99
+
100
+ def get_resize_config(self, media_input: MediaInput) -> dict:
101
+ if media_input['type'] == 'image':
102
+ w, h = media_input['image'].size
103
+ ret = navit_resize_image(
104
+ w, h, self.media_proc_cfg['patch_size'],
105
+ self.media_proc_cfg['merge_kernel_size'],
106
+ self.media_proc_cfg['in_patch_limit'],
107
+ self.media_proc_cfg['patch_limit_on_one_side'],
108
+ self.media_proc_cfg['fixed_output_tokens'])
109
+ return ret
110
+ elif media_input['type'] == 'video_chunk':
111
+ frame = media_input['video_chunk'][0]
112
+ width, height = frame.size
113
+ num_frames = len(media_input["video_chunk"])
114
+ fps = 1.0
115
+
116
+ sample_fps, max_num_frames_each_video = real_sample_fps_and_max_num_frames(
117
+ media_input["type"],
118
+ self.media_proc_cfg['sample_fps'],
119
+ self.media_proc_cfg['max_num_frames_each_video'],
120
+ )
121
+
122
+ in_patch_limit_each_frame = self.media_proc_cfg[
123
+ 'in_patch_limit_each_frame']
124
+ if in_patch_limit_each_frame is None:
125
+ in_patch_limit_each_frame = self.media_proc_cfg[
126
+ 'in_patch_limit']
127
+
128
+ ret = navit_resize_video(
129
+ width,
130
+ height,
131
+ num_frames,
132
+ fps,
133
+ sample_fps,
134
+ self.media_proc_cfg['patch_size'],
135
+ self.media_proc_cfg['merge_kernel_size'],
136
+ in_patch_limit_each_frame,
137
+ self.media_proc_cfg['patch_limit_on_one_side'],
138
+ self.media_proc_cfg['in_patch_limit_video'],
139
+ max_num_frames_each_video,
140
+ self.media_proc_cfg['fixed_output_tokens'],
141
+ )
142
+ return ret
143
+ else:
144
+ raise ValueError("Unsupported type: {}".format(
145
+ media_input['type']))
146
+
147
+ def resize_image(self, image: Image.Image, new_width: int, new_height: int,
148
+ pad_width: int, pad_height: int) -> np.ndarray:
149
+ image_np = image_to_np(image, (new_width, new_height), "resize")
150
+ image_np = np.pad(
151
+ image_np,
152
+ ((0, pad_height), (0, pad_width), (0, 0)),
153
+ mode="constant",
154
+ constant_values=0,
155
+ )
156
+ return image_np
157
+
158
+ def preprocess(
159
+ self,
160
+ medias: list[MediaInput],
161
+ return_tensors: Optional[Union[str, TensorType]] = None,
162
+ ) -> BatchFeature:
163
+ """
164
+ Preprocess a atom vision input (images/video_chunk) into model-ready tensors.
165
+
166
+ Args:
167
+ medias: List of MediaInput.
168
+ return_tensors: Desired output format ('pt', 'np', 'tf', or None).
169
+
170
+ Returns:
171
+ BatchFeature containing 'pixel_values' and 'grid_thws' tensors.
172
+ """
173
+ if not isinstance(medias, list):
174
+ medias = [medias]
175
+ if medias:
176
+ pixel_values = []
177
+ for item in medias:
178
+ item = ensure_media_type(item)
179
+ resize_config = self.get_resize_config(item)
180
+ new_width, new_height, pad_width, pad_height = resize_config[
181
+ 'new_width'], resize_config['new_height'], resize_config[
182
+ 'pad_width'], resize_config['pad_height']
183
+ if item['type'] == 'image':
184
+ image = item['image']
185
+ image_np = self.resize_image(image, new_width, new_height,
186
+ pad_width, pad_height)
187
+ pixel_values.append(np.expand_dims(image_np, axis=0))
188
+ elif item['type'] == 'video_chunk':
189
+ pixels = []
190
+ for frame in item['video_chunk']:
191
+ frame_np = self.resize_image(frame, new_width,
192
+ new_height, pad_width,
193
+ pad_height)
194
+ pixels.append(frame_np)
195
+ pixel_values.append(np.stack(pixels, axis=0))
196
+ else:
197
+ raise ValueError("Unsupported type: {}".format(
198
+ item['type']))
199
+ normalized_pixel_values = []
200
+ image_std_inv = 1.0 / np.array(self.media_proc_cfg['image_std'])
201
+ image_mean = np.array(self.media_proc_cfg['image_mean'])
202
+ for pixels in pixel_values:
203
+ pixels = normalize(pixels, image_mean, image_std_inv)
204
+ pixels_and_thw = navit_patchify(
205
+ pixels,
206
+ self.media_proc_cfg['patch_size'],
207
+ )
208
+ normalized_pixel_values.append(pixels_and_thw)
209
+
210
+ pixel_values = torch.cat([
211
+ _to_tensor(pixel_value['pixel_values'])
212
+ for pixel_value in normalized_pixel_values
213
+ ])
214
+ grid_thws = torch.cat([
215
+ _to_tensor(pixel_value['grid_thw'],
216
+ dtype=torch.int64).unsqueeze(0)
217
+ for pixel_value in normalized_pixel_values
218
+ ])
219
+
220
+ data = {
221
+ 'pixel_values': pixel_values,
222
+ 'grid_thws': grid_thws,
223
+ }
224
+
225
+ else:
226
+ data = {}
227
+
228
+ return BatchFeature(data=data, tensor_type=return_tensors)
229
+
230
+ def __repr__(self):
231
+ return f"KimiK25VisionProcessor(media_proc_cfg={self.media_proc_cfg})"
232
+
233
+ def to_dict(self) -> Dict[str, Any]:
234
+ output = super().to_dict()
235
+ output["media_proc_cfg"] = self.media_proc_cfg
236
+ if "media_processor" in output:
237
+ del output["media_processor"]
238
+ return output
239
+
240
+ @classmethod
241
+ def from_dict(cls, config_dict: Dict[str, Any], **kwargs):
242
+ config = config_dict.copy()
243
+ media_proc_cfg = config.pop("media_proc_cfg", {})
244
+ return cls(media_proc_cfg=media_proc_cfg, **config, **kwargs)
245
+
246
+ def to_json_string(self):
247
+ dictionary = self.to_dict()
248
+ for key, value in dictionary.items():
249
+ if hasattr(value, 'tolist'):
250
+ dictionary[key] = value.tolist()
251
+ return json.dumps(dictionary, indent=2, sort_keys=True) + "\n"
media_utils.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import io
3
+ import math
4
+ import os
5
+ from datetime import datetime, timezone
6
+ from typing import List, Literal, Optional, TypedDict
7
+
8
+ import numpy as np
9
+ from PIL import Image
10
+ from pydantic import BaseModel, Field
11
+
12
+ try:
13
+ from mecord import VideoReader
14
+ except ImportError:
15
+ VideoReader = None
16
+
17
+
18
+ class VideoSpec(BaseModel):
19
+ media_type: str = Literal['video']
20
+ height: int = Field(..., gt=0, description="video frame height")
21
+ width: int = Field(..., gt=0, description="video frame width")
22
+ num_frames: int = Field(..., gt=0, description="num frames")
23
+ fps: float = Field(..., gt=0, description="average fps")
24
+
25
+ # optional, help to accelerate video reading
26
+ key_indices: list[int] = Field(None, description="key indices")
27
+ frame_time_info: dict = Field(None, description="frame time info")
28
+
29
+
30
+ class ImageInput(TypedDict):
31
+ type: Literal['image']
32
+ image: Image.Image
33
+
34
+
35
+ class VideoChunkInput(TypedDict):
36
+ type: Literal['video_chunk']
37
+ video_chunk: List[Image.Image]
38
+ prompt: Optional[str] = None
39
+
40
+
41
+ MediaInput = ImageInput | VideoChunkInput
42
+
43
+
44
+ def get_video_meta(video_src: bytes | str | os.PathLike,
45
+ accurate: bool = True) -> dict:
46
+ """Get the dimensions of a video."""
47
+ if isinstance(video_src, os.PathLike):
48
+ video_src = str(video_src)
49
+ # if b64 string, decode to bytes
50
+ if isinstance(video_src,
51
+ str) and video_src.startswith('data:video/mp4;base64,'):
52
+ video_src = base64.b64decode(video_src.split(',')[1])
53
+ video = VideoReader(video_src, auto_init=accurate, num_threads=1)
54
+ assert video.num_frames > 0, "Invalid video format."
55
+ assert video.original_width > 0 and video.original_height > 0, (
56
+ "Invalid video format.")
57
+ assert video.avg_fps > 0, "Invalid video format."
58
+ return VideoSpec(media_type='video',
59
+ height=video.original_height,
60
+ width=video.original_width,
61
+ num_frames=video.num_frames,
62
+ fps=video.avg_fps,
63
+ key_indices=video.key_indices,
64
+ frame_time_info=video.frame_time_info)
65
+
66
+
67
+ def timestamp_as_str(timestamp: float,
68
+ timestamp_mode: str = "hh:mm:ss.fff") -> str:
69
+ """Convert a timestamp to a string in the format of HH:MM:SS.mmm."""
70
+ if timestamp_mode == "hh:mm:ss.fff":
71
+ return (datetime.fromtimestamp(timestamp,
72
+ tz=timezone.utc).strftime("%H:%M:%S") +
73
+ f".{int((timestamp % 1) * 1000):03d}")
74
+ elif timestamp_mode == "mm:ss.fff":
75
+ return (datetime.fromtimestamp(timestamp,
76
+ tz=timezone.utc).strftime("%M:%S") +
77
+ f".{int((timestamp % 1) * 1000):03d}")
78
+ elif timestamp_mode == "mm:ss":
79
+ return datetime.fromtimestamp(timestamp,
80
+ tz=timezone.utc).strftime("%M:%S")
81
+ else:
82
+ raise ValueError(f"Invalid timestamp mode: {timestamp_mode}")
83
+
84
+
85
+ def navit_resize_image(
86
+ width: int,
87
+ height: int,
88
+ patch_size: int,
89
+ merge_kernel_size: int,
90
+ in_patch_limit: int,
91
+ patch_limit_on_one_side: int,
92
+ fixed_output_tokens: int | None,
93
+ ):
94
+ # Apply the patch limits.
95
+ s1 = math.sqrt(
96
+ in_patch_limit /
97
+ (max(1.0, width // patch_size) * max(1.0, height // patch_size)))
98
+ s2 = patch_limit_on_one_side * patch_size / width
99
+ s3 = patch_limit_on_one_side * patch_size / height
100
+ scale = min(1.0, s1, s2, s3)
101
+ new_w, new_h = max(1, int(width * scale)), max(1, int(height * scale))
102
+ new_w = min(new_w, patch_limit_on_one_side * patch_size)
103
+ new_h = min(new_h, patch_limit_on_one_side * patch_size)
104
+
105
+ # Calculate the padding to make the height and width divisible by the merge kernel size and patch size.
106
+ factor = merge_kernel_size * patch_size
107
+
108
+ pad_height = (factor - new_h % factor) % factor
109
+ pad_width = (factor - new_w % factor) % factor
110
+
111
+ if fixed_output_tokens is not None:
112
+ num_tokens = fixed_output_tokens
113
+ else:
114
+ # Calculate new dimensions after padding and patching
115
+ token_height = (new_h + pad_height) // factor
116
+ token_width = (new_w + pad_width) // factor
117
+
118
+ assert token_height * merge_kernel_size <= patch_limit_on_one_side, (
119
+ f"token_height {token_height} * merge_kernel_size {merge_kernel_size} > patch_limit_on_one_side {patch_limit_on_one_side}"
120
+ )
121
+ assert token_width * merge_kernel_size <= patch_limit_on_one_side, (
122
+ f"token_width {token_width} * merge_kernel_size {merge_kernel_size} > patch_limit_on_one_side {patch_limit_on_one_side}"
123
+ )
124
+
125
+ num_tokens = token_height * token_width
126
+ return {
127
+ "num_tokens": num_tokens,
128
+ "new_width": new_w,
129
+ "new_height": new_h,
130
+ "pad_width": pad_width,
131
+ "pad_height": pad_height,
132
+ "sampled_nframes": 1,
133
+ }
134
+
135
+
136
+ def navit_resize_video(
137
+ width: int,
138
+ height: int,
139
+ nframes: int,
140
+ avg_fps: float,
141
+ sample_fps: float,
142
+ patch_size: int,
143
+ merge_kernel_size: int,
144
+ in_patch_limit_each_frame: int,
145
+ patch_limit_on_one_side: int,
146
+ in_patch_limit_total: int | None,
147
+ max_num_frames_each_video: int | None,
148
+ fixed_output_tokens_each_frame: int | None,
149
+ ):
150
+ sample_fps = min(sample_fps, avg_fps)
151
+ # Calculate the number of frames to sample based on target FPS
152
+ sampled_nframes = max(round(nframes * sample_fps / avg_fps), 1)
153
+ if max_num_frames_each_video is not None:
154
+ sampled_nframes = min(sampled_nframes, max_num_frames_each_video)
155
+
156
+ if in_patch_limit_total is not None:
157
+ in_patch_limit_each_frame = min(
158
+ round(in_patch_limit_total / sampled_nframes),
159
+ in_patch_limit_each_frame)
160
+
161
+ ret = navit_resize_image(
162
+ width,
163
+ height,
164
+ patch_size,
165
+ merge_kernel_size,
166
+ in_patch_limit_each_frame,
167
+ patch_limit_on_one_side,
168
+ fixed_output_tokens_each_frame,
169
+ )
170
+ ret["sampled_nframes"] = sampled_nframes
171
+ return ret
172
+
173
+
174
+ def real_sample_fps_and_max_num_frames(
175
+ type_name: Literal["video", "video_chunk"],
176
+ sample_fps: float,
177
+ max_num_frames_each_video: int | None,
178
+ ) -> tuple[int, int | None]:
179
+ if type_name == "video":
180
+ return sample_fps, max_num_frames_each_video
181
+ elif type_name == "video_chunk":
182
+ max_num_frames_each_video = None
183
+ sample_fps = math.inf
184
+ return sample_fps, max_num_frames_each_video
185
+ else:
186
+ return math.inf, None
187
+
188
+
189
+ def _to_pil(data: str | bytes):
190
+ if isinstance(data, Image.Image):
191
+
192
+ return data.convert("RGB")
193
+ elif isinstance(data, str):
194
+ if data.startswith("data:"):
195
+ raw_base64 = data.split(",")[1]
196
+ return Image.open(io.BytesIO(
197
+ base64.b64decode(raw_base64))).convert("RGB")
198
+ else:
199
+ return Image.open(data).convert("RGB")
200
+ elif isinstance(data, bytes):
201
+ return Image.open(io.BytesIO(data)).convert("RGB")
202
+ else:
203
+ raise ValueError(f"Unsupported data type: {type(data)}")
204
+
205
+
206
+ def ensure_media_type(media: MediaInput) -> MediaInput:
207
+ if media['type'] == 'image':
208
+ media['image'] = _to_pil(media['image'])
209
+ return media
210
+ elif media['type'] == 'video_chunk':
211
+ media['video_chunk'] = [
212
+ _to_pil(frame) for frame in media['video_chunk']
213
+ ]
214
+ return media
215
+ else:
216
+ raise ValueError(f"Unsupported media type: {media['type']}")
217
+
218
+
219
+ def image_to_np(
220
+ image: Image.Image,
221
+ resize_to: tuple[int, int] | None = None,
222
+ mode: str = "resize",
223
+ raise_error_for_ill_resize: bool = True,
224
+ ) -> np.ndarray:
225
+ """Convert an image to a numpy array.
226
+
227
+ Args:
228
+ content: The image to convert.
229
+ resize_to: The size to resize the image to.
230
+ mode: The mode to resize the image to.
231
+ raise_error_for_ill_resize: Whether to raise an error for ill-sized resize.
232
+
233
+ Returns:
234
+ A numpy array.
235
+ """
236
+ assert isinstance(image, Image.Image), "image must be a PIL Image"
237
+ if resize_to is not None:
238
+ if mode == "resize":
239
+ image = image.resize(resize_to, resample=Image.Resampling.BICUBIC)
240
+
241
+ elif mode == "rescale_and_pad_to_center":
242
+ scale = min(resize_to[0] / image.width,
243
+ resize_to[1] / image.height, 1.0)
244
+ new_width = round(image.width * scale)
245
+ new_height = round(image.height * scale)
246
+ if new_width == 0 or new_height == 0:
247
+ if raise_error_for_ill_resize:
248
+ raise ValueError(
249
+ f"Invalid resize to: {resize_to}, from image size: {image.size}"
250
+ )
251
+ else:
252
+ return np.zeros((resize_to[1], resize_to[0], 3),
253
+ dtype=np.uint8)
254
+
255
+ image = image.resize((new_width, new_height),
256
+ resample=Image.Resampling.BICUBIC)
257
+ padding_left = (resize_to[0] - new_width) // 2
258
+ padding_right = resize_to[0] - new_width - padding_left
259
+ padding_top = (resize_to[1] - new_height) // 2
260
+ padding_bottom = resize_to[1] - new_height - padding_top
261
+ image = np.asarray(image)
262
+ image = np.pad(
263
+ image,
264
+ ((padding_top, padding_bottom), (padding_left, padding_right),
265
+ (0, 0)),
266
+ mode="constant",
267
+ constant_values=0,
268
+ )
269
+ assert image.shape == (resize_to[1], resize_to[0], 3)
270
+
271
+ elif mode == "rescale_and_pad_to_rightbottom":
272
+ scale = min(resize_to[0] / image.width,
273
+ resize_to[1] / image.height, 1.0)
274
+ new_width = round(image.width * scale)
275
+ new_height = round(image.height * scale)
276
+ if new_width == 0 or new_height == 0:
277
+ if raise_error_for_ill_resize:
278
+ raise ValueError(
279
+ f"Invalid resize to: {resize_to}, from image size: {image.size}"
280
+ )
281
+ else:
282
+ return np.zeros((resize_to[1], resize_to[0], 3),
283
+ dtype=np.uint8)
284
+
285
+ image = image.resize((new_width, new_height),
286
+ resample=Image.Resampling.BICUBIC)
287
+ padding_right = resize_to[0] - new_width
288
+ padding_bottom = resize_to[1] - new_height
289
+ image = np.asarray(image)
290
+ image = np.pad(
291
+ image,
292
+ ((0, padding_bottom), (0, padding_right), (0, 0)),
293
+ mode="constant",
294
+ constant_values=0,
295
+ )
296
+ assert image.shape == (resize_to[1], resize_to[0], 3)
297
+
298
+ else:
299
+ raise ValueError(f"Invalid mode: {mode}")
300
+
301
+ if isinstance(image, Image.Image):
302
+ return np.asarray(image)
303
+ else:
304
+ return image
305
+
306
+
307
+ def navit_patchify(pixel_values: np.ndarray,
308
+ patch_size: int) -> dict[str, np.ndarray]:
309
+ """Reshape the pixel values to a navit shape.
310
+
311
+ Args:
312
+ pixel_values: np.ndarray, shape (t, h, w, c)
313
+ patch_size: int
314
+
315
+ Returns:
316
+ dict[str, np.ndarray]
317
+ - patches: np.ndarray, shape (t * h//patch_size * w//patch_size, c, patch_size, patch_size)
318
+ - grid_thw: np.ndarray, (t, h//patch_size, w//patch_size)
319
+ """
320
+ T, H, W, C = pixel_values.shape
321
+ assert C == 3, "pixel_values must have 3 channels"
322
+
323
+ patches = pixel_values.reshape(T, H // patch_size, patch_size,
324
+ W // patch_size, patch_size, C)
325
+ # (T, H//patch_size, W//patch_size, C, patch_size, patch_size)
326
+ patches = patches.transpose(0, 1, 3, 5, 2, 4)
327
+ patches = patches.reshape(-1, C, patch_size, patch_size)
328
+ grid_thw = np.array([T, H // patch_size, W // patch_size])
329
+ return {"pixel_values": patches, "grid_thw": grid_thw}
330
+
331
+
332
+ def normalize(x: np.ndarray,
333
+ mean,
334
+ std_inv,
335
+ pixels_dtype: np.dtype = np.float32) -> np.ndarray:
336
+ """Normalize the image.
337
+
338
+ Args:
339
+ x: The image to normalize. The shape is (..., 3). The dtype is uint8. The range is [0, 255].
340
+ mean: The mean of the image.
341
+ std_inv: The inverse of the std of the image.
342
+ pixels_dtype: The dtype of the image.
343
+ Returns:
344
+ The normalized image. The shape is (..., 3). The dtype is determined by the pixels_dtype.
345
+ """
346
+ x = (x / 255.0).astype(pixels_dtype)
347
+ x -= mean
348
+ x *= std_inv
349
+ return x
350
+
351
+
352
+ def _to_tensor(data, **kwargs):
353
+ import torch
354
+
355
+ if isinstance(data, np.ndarray):
356
+ return torch.from_numpy(data).to(**kwargs)
357
+ elif isinstance(data, torch.Tensor):
358
+ return data.to(**kwargs)
359
+ elif isinstance(data, list):
360
+ return [_to_tensor(item, **kwargs) for item in data]
361
+ elif isinstance(data, tuple):
362
+ return tuple(_to_tensor(item, **kwargs) for item in data)
363
+ elif isinstance(data, dict):
364
+ return {k: _to_tensor(v, **kwargs) for k, v in data.items()}
365
+ elif data is None:
366
+ return None
367
+ else:
368
+ raise ValueError(f"Unsupported data type: {type(data)}")
model-00002-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9899acb078f792d43caea747baed7ec113660874cef36f8848d11375a0fc5438
3
+ size 3523215810
model-00007-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:30739f75fad70052b94491407558cbd90b4ad65b4311807691aae8235c212896
3
+ size 3523215816
model-00008-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:71c303920ae5d5dbb48a0c456f34fa014f08515fa2106be4e16d9b5e0adf2060
3
+ size 3523215810
model-00010-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:85a73ddd5345afe71100a97bb81cb06f8a39ebb457a13a69e09a3be7470a896b
3
+ size 3523215816
model-00022-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d149506393ab6fc5525249a88af40025e3b02b356c642da04fa331b52adbd014
3
+ size 3523215816
model-00027-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b207ef3622f04c66b4c9469648b706620290c60b64e9ba61f567737a645254b2
3
+ size 3610413291
model-00030-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c7e29ff6df92958c2c4804bd5a10c0eafb584cfb04b6eb6c8b0bda28453c108e
3
+ size 3610413293
model-00035-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3fc36b372aaafe1d52c9bc19f2b2e7e90d66c62a0e1cfe585472f9e5d0affe90
3
+ size 3523215813
model-00043-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8df90312159dce06cfbb0b1749da0652f7f36807f358cea8c037c99338eda823
3
+ size 3523215819
model-00046-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:213fb793968e556bcff369dd50ea810edf345ef04984bef7f63118331c2187f9
3
+ size 3523215819
model-00049-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9a04317b37edefd5189522c4e10ee621fc882ad922c2fbe0a9f73a8714ac7b31
3
+ size 3523215819
model-00063-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f8874e6736a81a0c0f6a4255c23122d51b7df809eaf1ff63bc5bd3fd8641a160
3
+ size 3610413295
model-00071-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1fadf851758080b4922adcc467cd367bad0521a6f06e12d5c7416a5e71a4a31c
3
+ size 3523215813
model-00074-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d6f72d554cd157cde1920b834704fcc05771b3488f00d774291b0c1b20b8677
3
+ size 3523215813
model-00082-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:95f43c093ab04aa95f5c69275c353d156464f038d1aa970e2b8355d1825b4a9d
3
+ size 3523215819
model-00087-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:697cd730e1a991f8735ca9696f3f4c21cd7cd4e8f7b5d3c95115b9efa06246a0
3
+ size 3610413303
model-00088-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5a8b4462396c7776bf875eaf0aa17e8ec19cab58e91eb4b55e9b03b9598821a8
3
+ size 3523215819
model-00090-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ad166ef9197a1b15a8e97a8ccaad6d990184f2d617465b85c533b6b9e890e751
3
+ size 3610413333
model-00095-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f22e4c7186ab1ce141fcf22136d1cd7e22378b2d9668e3ff30a4967d13d84d75
3
+ size 3523215813
model-00103-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cc8bd31f5f0574ed24078f71b89725efee2cd51f722688fc5fd4ab6c0d30bc2a
3
+ size 3523215819
model-00106-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a399318fbcb38abf0134ba5fe8e8b588b5f08ab6796882486fdc578ce96b78f4
3
+ size 3523215819
model-00109-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3ddf001e7b2baa002d52713dec3c4c27429b9cd198948712aa69912afe75119f
3
+ size 3523215819
model-00111-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e4a89ba1bdd4bd38ffaf923b984820f70e0c3e2475f79d47360701d44adbe8fa
3
+ size 3610413319
model-00114-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2988e6935cf64612c63121ed5cb444184ec965b6bc26ca5e8c5b8b2c073d86a0
3
+ size 3610413321
model-00123-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0ad772c74eb8bfa73ca196b15df012b830b51cc9a8ad245e26a08623cf8108c4
3
+ size 3610413289
model-00126-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:11625c31ad1d636d3bcc8391b3d32db7634b6a0f8c20f1e97a1650344a5c529a
3
+ size 3610413321
model-00129-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cea0e57ee0da8697a38d1bf40ca4028a92e2ffaf2341183f5c54f61eae3d2b78
3
+ size 3610413301
model-00142-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ad563473f9ab0a9175c2c3b0fea2b22576a106651d7ec520ca0bf9c8d25f4a4e
3
+ size 3523215819
model-00147-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6b7453fdc35be0d4ecadf267d2137038587de56ad3f723259b7438baa4bdaaa9
3
+ size 3610413329
model-00148-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:032c1f7b66245c0707219fec2185755dca2cbe9fa84d43cf9ad8c6241363c55a
3
+ size 3523215819
model-00150-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fb7f07e334a932847ec2e0735db32114830eee880d00d4ceb034c893a8e203a1
3
+ size 3610413305
model-00155-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fbbf364b74981a7d0e8a784af36acbb1597b98e61ce4628c31dbcb199dd93d56
3
+ size 3523215813
model-00162-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cea9caff9ee970c6bf3d7258ef08bd4b3fcc649a2ab5c7d8e0094ea57c3a72ca
3
+ size 3610413323
model-00167-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:71ac85db4d718ed6c35358512da9eec7d1dcfac003a3b07caf7168498cb7293a
3
+ size 3523215813
model-00168-of-00180.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:64155e7b61f5284c6fee55687168f31ba1581a4f43c5abc41008f052f4625dc5
3
+ size 3610413389
model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
modeling_deepseek.py ADDED
@@ -0,0 +1,1815 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2023 DeepSeek-AI and The HuggingFace Inc. team. All rights reserved.
3
+ #
4
+ # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
5
+ # and OPT implementations in this library. It has been modified from its
6
+ # original forms to accommodate minor architectural differences compared
7
+ # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ """ PyTorch DeepSeek model."""
21
+ import math
22
+ import warnings
23
+ from typing import List, Optional, Tuple, Union
24
+
25
+ import numpy as np
26
+ import torch
27
+ import torch.distributed as dist
28
+ import torch.nn.functional as F
29
+ import torch.utils.checkpoint
30
+ from torch import nn
31
+ from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
32
+ from transformers.activations import ACT2FN
33
+ from transformers.cache_utils import Cache, DynamicCache
34
+ from transformers.modeling_attn_mask_utils import \
35
+ _prepare_4d_causal_attention_mask
36
+ from transformers.modeling_outputs import (BaseModelOutputWithPast,
37
+ CausalLMOutputWithPast,
38
+ SequenceClassifierOutputWithPast)
39
+ from transformers.modeling_utils import PreTrainedModel
40
+ from transformers.pytorch_utils import (ALL_LAYERNORM_LAYERS,
41
+ is_torch_greater_or_equal_than_1_13)
42
+ from transformers.utils import (add_start_docstrings,
43
+ add_start_docstrings_to_model_forward,
44
+ is_flash_attn_2_available,
45
+ is_flash_attn_greater_or_equal_2_10, logging,
46
+ replace_return_docstrings)
47
+
48
+ try:
49
+ from transformers.utils.import_utils import is_torch_fx_available
50
+ except ImportError:
51
+
52
+ def is_torch_fx_available() -> bool:
53
+ return hasattr(torch, "fx")
54
+
55
+
56
+ from .configuration_deepseek import DeepseekV3Config
57
+
58
+ if is_flash_attn_2_available():
59
+ from flash_attn import flash_attn_func, flash_attn_varlen_func
60
+ from flash_attn.bert_padding import pad_input # noqa
61
+ from flash_attn.bert_padding import index_first_axis, unpad_input
62
+
63
+ # This makes `_prepare_4d_causal_attention_mask` a leaf function in the FX graph.
64
+ # It means that the function will not be traced through and simply appear as a node in the graph.
65
+ if is_torch_fx_available():
66
+ if not is_torch_greater_or_equal_than_1_13:
67
+ import torch.fx
68
+
69
+ _prepare_4d_causal_attention_mask = torch.fx.wrap(
70
+ _prepare_4d_causal_attention_mask)
71
+
72
+ logger = logging.get_logger(__name__)
73
+
74
+ _CONFIG_FOR_DOC = "DeepseekV3Config"
75
+
76
+
77
+ def _get_unpad_data(attention_mask):
78
+ seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32)
79
+ indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
80
+ max_seqlen_in_batch = seqlens_in_batch.max().item()
81
+ cu_seqlens = F.pad(
82
+ torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.torch.int32), (1, 0))
83
+ return (
84
+ indices,
85
+ cu_seqlens,
86
+ max_seqlen_in_batch,
87
+ )
88
+
89
+
90
+ # code modified from transformers 4.48.3 to amend breaks in newer transformers versions
91
+ def get_usable_length(past_key_value,
92
+ new_seq_length: int,
93
+ layer_idx: Optional[int] = 0) -> int:
94
+ max_length = past_key_value.get_max_cache_shape()
95
+ previous_seq_length = past_key_value.get_seq_length(layer_idx)
96
+ if max_length is not None and max_length > 0 and previous_seq_length + new_seq_length > max_length:
97
+ return max_length - new_seq_length
98
+ return previous_seq_length
99
+
100
+
101
+ class DeepseekV3RMSNorm(nn.Module):
102
+
103
+ def __init__(self, hidden_size, eps=1e-6):
104
+ """
105
+ DeepseekV3RMSNorm is equivalent to T5LayerNorm
106
+ """
107
+ super().__init__()
108
+ self.weight = nn.Parameter(torch.ones(hidden_size))
109
+ self.variance_epsilon = eps
110
+
111
+ def forward(self, hidden_states):
112
+ input_dtype = hidden_states.dtype
113
+ hidden_states = hidden_states.to(torch.float32)
114
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
115
+ hidden_states = hidden_states * torch.rsqrt(variance +
116
+ self.variance_epsilon)
117
+ return self.weight * hidden_states.to(input_dtype)
118
+
119
+
120
+ ALL_LAYERNORM_LAYERS.append(DeepseekV3RMSNorm)
121
+
122
+
123
+ class DeepseekV3RotaryEmbedding(nn.Module):
124
+
125
+ def __init__(self,
126
+ dim,
127
+ max_position_embeddings=2048,
128
+ base=10000,
129
+ device=None):
130
+ super().__init__()
131
+
132
+ self.dim = dim
133
+ self.max_position_embeddings = max_position_embeddings
134
+ self.base = base
135
+ inv_freq = 1.0 / (self.base**(
136
+ torch.arange(0, self.dim, 2).float().to(device) / self.dim))
137
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
138
+
139
+ # Build here to make `torch.jit.trace` work.
140
+ self._set_cos_sin_cache(
141
+ seq_len=max_position_embeddings,
142
+ device=self.inv_freq.device,
143
+ dtype=torch.get_default_dtype(),
144
+ )
145
+ self.max_seq_len_cached = None
146
+
147
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
148
+ self.max_seq_len_cached = seq_len
149
+ t = torch.arange(self.max_seq_len_cached,
150
+ device=device,
151
+ dtype=self.inv_freq.dtype)
152
+
153
+ freqs = torch.outer(t, self.inv_freq.to(t.device))
154
+ # Different from paper, but it uses a different permutation in order to obtain the same calculation
155
+ emb = torch.cat((freqs, freqs), dim=-1)
156
+ self.register_buffer("cos_cached",
157
+ emb.cos().to(dtype),
158
+ persistent=False)
159
+ self.register_buffer("sin_cached",
160
+ emb.sin().to(dtype),
161
+ persistent=False)
162
+
163
+ def forward(self, x, seq_len=None):
164
+ # x: [bs, num_attention_heads, seq_len, head_size]
165
+ if self.max_seq_len_cached is None or seq_len > self.max_seq_len_cached:
166
+ self._set_cos_sin_cache(seq_len=seq_len,
167
+ device=x.device,
168
+ dtype=x.dtype)
169
+
170
+ return (
171
+ self.cos_cached[:seq_len].to(dtype=x.dtype),
172
+ self.sin_cached[:seq_len].to(dtype=x.dtype),
173
+ )
174
+
175
+
176
+ # Copied from transformers.models.llama.modeling_llama.LlamaLinearScalingRotaryEmbedding with Llama->DeepseekV3
177
+ class DeepseekV3LinearScalingRotaryEmbedding(DeepseekV3RotaryEmbedding):
178
+ """DeepseekV3RotaryEmbedding extended with linear scaling. Credits to the Reddit user /u/kaiokendev"""
179
+
180
+ def __init__(
181
+ self,
182
+ dim,
183
+ max_position_embeddings=2048,
184
+ base=10000,
185
+ device=None,
186
+ scaling_factor=1.0,
187
+ ):
188
+ self.scaling_factor = scaling_factor
189
+ super().__init__(dim, max_position_embeddings, base, device)
190
+
191
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
192
+ self.max_seq_len_cached = seq_len
193
+ t = torch.arange(self.max_seq_len_cached,
194
+ device=device,
195
+ dtype=self.inv_freq.dtype)
196
+ t = t / self.scaling_factor
197
+
198
+ freqs = torch.outer(t, self.inv_freq)
199
+ # Different from paper, but it uses a different permutation in order to obtain the same calculation
200
+ emb = torch.cat((freqs, freqs), dim=-1)
201
+ self.register_buffer("cos_cached",
202
+ emb.cos().to(dtype),
203
+ persistent=False)
204
+ self.register_buffer("sin_cached",
205
+ emb.sin().to(dtype),
206
+ persistent=False)
207
+
208
+
209
+ # Copied from transformers.models.llama.modeling_llama.LlamaDynamicNTKScalingRotaryEmbedding with Llama->DeepseekV3
210
+ class DeepseekV3DynamicNTKScalingRotaryEmbedding(DeepseekV3RotaryEmbedding):
211
+ """DeepseekV3RotaryEmbedding extended with Dynamic NTK scaling. Credits to the Reddit users /u/bloc97 and /u/emozilla"""
212
+
213
+ def __init__(
214
+ self,
215
+ dim,
216
+ max_position_embeddings=2048,
217
+ base=10000,
218
+ device=None,
219
+ scaling_factor=1.0,
220
+ ):
221
+ self.scaling_factor = scaling_factor
222
+ super().__init__(dim, max_position_embeddings, base, device)
223
+
224
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
225
+ self.max_seq_len_cached = seq_len
226
+
227
+ if seq_len > self.max_position_embeddings:
228
+ base = self.base * ((self.scaling_factor * seq_len /
229
+ self.max_position_embeddings) -
230
+ (self.scaling_factor - 1))**(self.dim /
231
+ (self.dim - 2))
232
+ inv_freq = 1.0 / (base**(
233
+ torch.arange(0, self.dim, 2).float().to(device) / self.dim))
234
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
235
+
236
+ t = torch.arange(self.max_seq_len_cached,
237
+ device=device,
238
+ dtype=self.inv_freq.dtype)
239
+
240
+ freqs = torch.outer(t, self.inv_freq)
241
+ # Different from paper, but it uses a different permutation in order to obtain the same calculation
242
+ emb = torch.cat((freqs, freqs), dim=-1)
243
+ self.register_buffer("cos_cached",
244
+ emb.cos().to(dtype),
245
+ persistent=False)
246
+ self.register_buffer("sin_cached",
247
+ emb.sin().to(dtype),
248
+ persistent=False)
249
+
250
+
251
+ # Inverse dim formula to find dim based on number of rotations
252
+ def yarn_find_correction_dim(num_rotations,
253
+ dim,
254
+ base=10000,
255
+ max_position_embeddings=2048):
256
+ return (dim * math.log(max_position_embeddings /
257
+ (num_rotations * 2 * math.pi))) / (2 *
258
+ math.log(base))
259
+
260
+
261
+ # Find dim range bounds based on rotations
262
+ def yarn_find_correction_range(low_rot,
263
+ high_rot,
264
+ dim,
265
+ base=10000,
266
+ max_position_embeddings=2048):
267
+ low = math.floor(
268
+ yarn_find_correction_dim(low_rot, dim, base, max_position_embeddings))
269
+ high = math.ceil(
270
+ yarn_find_correction_dim(high_rot, dim, base, max_position_embeddings))
271
+ return max(low, 0), min(high, dim - 1) # Clamp values just in case
272
+
273
+
274
+ def yarn_get_mscale(scale=1, mscale=1):
275
+ if scale <= 1:
276
+ return 1.0
277
+ return 0.1 * mscale * math.log(scale) + 1.0
278
+
279
+
280
+ def yarn_linear_ramp_mask(min, max, dim):
281
+ if min == max:
282
+ max += 0.001 # Prevent singularity
283
+
284
+ linear_func = (torch.arange(dim, dtype=torch.float32) - min) / (max - min)
285
+ ramp_func = torch.clamp(linear_func, 0, 1)
286
+ return ramp_func
287
+
288
+
289
+ class DeepseekV3YarnRotaryEmbedding(DeepseekV3RotaryEmbedding):
290
+
291
+ def __init__(
292
+ self,
293
+ dim,
294
+ max_position_embeddings=2048,
295
+ base=10000,
296
+ device=None,
297
+ scaling_factor=1.0,
298
+ original_max_position_embeddings=4096,
299
+ beta_fast=32,
300
+ beta_slow=1,
301
+ mscale=1,
302
+ mscale_all_dim=0,
303
+ ):
304
+ self.scaling_factor = scaling_factor
305
+ self.original_max_position_embeddings = original_max_position_embeddings
306
+ self.beta_fast = beta_fast
307
+ self.beta_slow = beta_slow
308
+ self.mscale = mscale
309
+ self.mscale_all_dim = mscale_all_dim
310
+ super().__init__(dim, max_position_embeddings, base, device)
311
+
312
+ def _set_cos_sin_cache(self, seq_len, device, dtype):
313
+ self.max_seq_len_cached = seq_len
314
+ dim = self.dim
315
+
316
+ freq_extra = 1.0 / (self.base**(
317
+ torch.arange(0, dim, 2, dtype=torch.float32, device=device) / dim))
318
+ freq_inter = 1.0 / (self.scaling_factor * self.base**(
319
+ torch.arange(0, dim, 2, dtype=torch.float32, device=device) / dim))
320
+
321
+ low, high = yarn_find_correction_range(
322
+ self.beta_fast,
323
+ self.beta_slow,
324
+ dim,
325
+ self.base,
326
+ self.original_max_position_embeddings,
327
+ )
328
+ inv_freq_mask = 1.0 - yarn_linear_ramp_mask(low, high, dim // 2).to(
329
+ device=device, dtype=torch.float32)
330
+ inv_freq = freq_inter * (1 -
331
+ inv_freq_mask) + freq_extra * inv_freq_mask
332
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
333
+
334
+ t = torch.arange(seq_len, device=device, dtype=torch.float32)
335
+
336
+ freqs = torch.outer(t, inv_freq)
337
+
338
+ _mscale = float(
339
+ yarn_get_mscale(self.scaling_factor, self.mscale) /
340
+ yarn_get_mscale(self.scaling_factor, self.mscale_all_dim))
341
+
342
+ emb = torch.cat((freqs, freqs), dim=-1)
343
+ self.register_buffer("cos_cached", (emb.cos() * _mscale).to(dtype),
344
+ persistent=False)
345
+ self.register_buffer("sin_cached", (emb.sin() * _mscale).to(dtype),
346
+ persistent=False)
347
+
348
+
349
+ # Copied from transformers.models.llama.modeling_llama.rotate_half
350
+ def rotate_half(x):
351
+ """Rotates half the hidden dims of the input."""
352
+ x1 = x[..., :x.shape[-1] // 2]
353
+ x2 = x[..., x.shape[-1] // 2:]
354
+ return torch.cat((-x2, x1), dim=-1)
355
+
356
+
357
+ # Copied from transformers.models.llama.modeling_llama.apply_rotary_pos_emb
358
+ def apply_rotary_pos_emb(q, k, cos, sin, position_ids, unsqueeze_dim=1):
359
+ """Applies Rotary Position Embedding to the query and key tensors.
360
+
361
+ Args:
362
+ q (`torch.Tensor`): The query tensor.
363
+ k (`torch.Tensor`): The key tensor.
364
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
365
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
366
+ position_ids (`torch.Tensor`):
367
+ The position indices of the tokens corresponding to the query and key tensors. For example, this can be
368
+ used to pass offsetted position ids when working with a KV-cache.
369
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
370
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
371
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
372
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
373
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
374
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
375
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
376
+ Returns:
377
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
378
+ """
379
+ cos = cos[position_ids].unsqueeze(unsqueeze_dim)
380
+ sin = sin[position_ids].unsqueeze(unsqueeze_dim)
381
+
382
+ b, h, s, d = q.shape
383
+ q = q.view(b, h, s, d // 2, 2).transpose(4, 3).reshape(b, h, s, d)
384
+
385
+ b, h, s, d = k.shape
386
+ k = k.view(b, h, s, d // 2, 2).transpose(4, 3).reshape(b, h, s, d)
387
+
388
+ q_embed = (q * cos) + (rotate_half(q) * sin)
389
+ k_embed = (k * cos) + (rotate_half(k) * sin)
390
+ return q_embed, k_embed
391
+
392
+
393
+ class DeepseekV3MLP(nn.Module):
394
+
395
+ def __init__(self, config, hidden_size=None, intermediate_size=None):
396
+ super().__init__()
397
+ self.config = config
398
+ self.hidden_size = config.hidden_size if hidden_size is None else hidden_size
399
+ self.intermediate_size = (config.intermediate_size if intermediate_size
400
+ is None else intermediate_size)
401
+
402
+ self.gate_proj = nn.Linear(self.hidden_size,
403
+ self.intermediate_size,
404
+ bias=False)
405
+ self.up_proj = nn.Linear(self.hidden_size,
406
+ self.intermediate_size,
407
+ bias=False)
408
+ self.down_proj = nn.Linear(self.intermediate_size,
409
+ self.hidden_size,
410
+ bias=False)
411
+ self.act_fn = ACT2FN[config.hidden_act]
412
+
413
+ def forward(self, x):
414
+ down_proj = self.down_proj(
415
+ self.act_fn(self.gate_proj(x)) * self.up_proj(x))
416
+ return down_proj
417
+
418
+
419
+ class MoEGate(nn.Module):
420
+
421
+ def __init__(self, config):
422
+ super().__init__()
423
+ self.config = config
424
+ self.top_k = config.num_experts_per_tok
425
+ self.n_routed_experts = config.n_routed_experts
426
+ self.routed_scaling_factor = config.routed_scaling_factor
427
+ self.scoring_func = config.scoring_func
428
+ self.seq_aux = config.seq_aux
429
+ self.topk_method = config.topk_method
430
+ self.n_group = config.n_group
431
+ self.topk_group = config.topk_group
432
+
433
+ # topk selection algorithm
434
+ self.norm_topk_prob = config.norm_topk_prob
435
+ self.gating_dim = config.hidden_size
436
+ self.weight = nn.Parameter(
437
+ torch.empty((self.n_routed_experts, self.gating_dim)))
438
+ if self.topk_method == "noaux_tc":
439
+ self.e_score_correction_bias = nn.Parameter(
440
+ torch.empty((self.n_routed_experts)))
441
+ self.reset_parameters()
442
+
443
+ def reset_parameters(self) -> None:
444
+ import torch.nn.init as init
445
+
446
+ init.kaiming_uniform_(self.weight, a=math.sqrt(5))
447
+
448
+ def forward(self, hidden_states):
449
+ bsz, seq_len, h = hidden_states.shape
450
+ ### compute gating score
451
+ hidden_states = hidden_states.view(-1, h)
452
+ logits = F.linear(hidden_states.type(torch.float32),
453
+ self.weight.type(torch.float32), None)
454
+ if self.scoring_func == "sigmoid":
455
+ scores = logits.sigmoid()
456
+ else:
457
+ raise NotImplementedError(
458
+ f"insupportable scoring function for MoE gating: {self.scoring_func}"
459
+ )
460
+
461
+ ### select top-k experts
462
+ if self.topk_method == "noaux_tc":
463
+ assert not self.training
464
+ scores_for_choice = scores.view(
465
+ bsz * seq_len, -1) + self.e_score_correction_bias.unsqueeze(0)
466
+ group_scores = (scores_for_choice.view(
467
+ bsz * seq_len, self.n_group,
468
+ -1).topk(2, dim=-1)[0].sum(dim=-1)) # [n, n_group]
469
+ group_idx = torch.topk(group_scores,
470
+ k=self.topk_group,
471
+ dim=-1,
472
+ sorted=False)[1] # [n, top_k_group]
473
+ group_mask = torch.zeros_like(group_scores) # [n, n_group]
474
+ group_mask.scatter_(1, group_idx, 1) # [n, n_group]
475
+ score_mask = (group_mask.unsqueeze(-1).expand(
476
+ bsz * seq_len, self.n_group,
477
+ self.n_routed_experts // self.n_group).reshape(
478
+ bsz * seq_len, -1)) # [n, e]
479
+ tmp_scores = scores_for_choice.masked_fill(~score_mask.bool(),
480
+ 0.0) # [n, e]
481
+ _, topk_idx = torch.topk(tmp_scores,
482
+ k=self.top_k,
483
+ dim=-1,
484
+ sorted=False)
485
+ topk_weight = scores.gather(1, topk_idx)
486
+ else:
487
+ raise NotImplementedError(
488
+ f"insupportable TopK function for MoE gating: {self.topk_method}"
489
+ )
490
+
491
+ ### norm gate to sum 1
492
+ if self.top_k > 1 and self.norm_topk_prob:
493
+ denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
494
+ topk_weight = topk_weight / denominator
495
+ topk_weight = topk_weight * self.routed_scaling_factor # must multiply the scaling factor
496
+
497
+ return topk_idx, topk_weight
498
+
499
+
500
+ class DeepseekV3MoE(nn.Module):
501
+ """
502
+ A mixed expert module containing shared experts.
503
+ """
504
+
505
+ def __init__(self, config):
506
+ super().__init__()
507
+ self.config = config
508
+ self.num_experts_per_tok = config.num_experts_per_tok
509
+
510
+ if hasattr(config, "ep_size") and config.ep_size > 1:
511
+ assert config.ep_size == dist.get_world_size()
512
+ self.ep_size = config.ep_size
513
+ self.experts_per_rank = config.n_routed_experts // config.ep_size
514
+ self.ep_rank = dist.get_rank()
515
+ self.experts = nn.ModuleList([
516
+ (DeepseekV3MLP(config,
517
+ intermediate_size=config.moe_intermediate_size)
518
+ if i >= self.ep_rank * self.experts_per_rank
519
+ and i < (self.ep_rank + 1) * self.experts_per_rank else None)
520
+ for i in range(config.n_routed_experts)
521
+ ])
522
+ else:
523
+ self.ep_size = 1
524
+ self.experts_per_rank = config.n_routed_experts
525
+ self.ep_rank = 0
526
+ self.experts = nn.ModuleList([
527
+ DeepseekV3MLP(config,
528
+ intermediate_size=config.moe_intermediate_size)
529
+ for i in range(config.n_routed_experts)
530
+ ])
531
+ self.gate = MoEGate(config)
532
+ if config.n_shared_experts is not None:
533
+ intermediate_size = config.moe_intermediate_size * config.n_shared_experts
534
+ self.shared_experts = DeepseekV3MLP(
535
+ config=config, intermediate_size=intermediate_size)
536
+
537
+ def forward(self, hidden_states):
538
+ identity = hidden_states
539
+ orig_shape = hidden_states.shape
540
+ topk_idx, topk_weight = self.gate(hidden_states)
541
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
542
+ # flat_topk_idx = topk_idx.view(-1)
543
+ if not self.training:
544
+ y = self.moe_infer(hidden_states, topk_idx,
545
+ topk_weight).view(*orig_shape)
546
+ if self.config.n_shared_experts is not None:
547
+ y = y + self.shared_experts(identity)
548
+ return y
549
+
550
+ @torch.no_grad()
551
+ def moe_infer(self, x, topk_ids, topk_weight):
552
+ cnts = topk_ids.new_zeros((topk_ids.shape[0], len(self.experts)))
553
+ cnts.scatter_(1, topk_ids, 1)
554
+ tokens_per_expert = cnts.sum(dim=0)
555
+ idxs = topk_ids.view(-1).argsort()
556
+ sorted_tokens = x[idxs // topk_ids.shape[1]]
557
+ sorted_tokens_shape = sorted_tokens.shape
558
+ if self.ep_size > 1:
559
+ tokens_per_ep_rank = tokens_per_expert.view(self.ep_size,
560
+ -1).sum(dim=1)
561
+ tokens_per_expert_group = tokens_per_expert.new_empty(
562
+ tokens_per_expert.shape[0])
563
+ dist.all_to_all_single(tokens_per_expert_group, tokens_per_expert)
564
+ output_splits = (tokens_per_expert_group.view(
565
+ self.ep_size, -1).sum(1).cpu().numpy().tolist())
566
+ gathered_tokens = sorted_tokens.new_empty(
567
+ tokens_per_expert_group.sum(dim=0).cpu().item(),
568
+ sorted_tokens.shape[1])
569
+ input_split_sizes = tokens_per_ep_rank.cpu().numpy().tolist()
570
+ dist.all_to_all(
571
+ list(gathered_tokens.split(output_splits)),
572
+ list(sorted_tokens.split(input_split_sizes)),
573
+ )
574
+ tokens_per_expert_post_gather = tokens_per_expert_group.view(
575
+ self.ep_size, self.experts_per_rank).sum(dim=0)
576
+ gatherd_idxs = np.zeros(shape=(gathered_tokens.shape[0], ),
577
+ dtype=np.int32)
578
+ s = 0
579
+ for i, k in enumerate(tokens_per_expert_group.cpu().numpy()):
580
+ gatherd_idxs[s:s + k] = i % self.experts_per_rank
581
+ s += k
582
+ gatherd_idxs = gatherd_idxs.argsort()
583
+ sorted_tokens = gathered_tokens[gatherd_idxs]
584
+ tokens_per_expert = tokens_per_expert_post_gather
585
+ tokens_per_expert = tokens_per_expert.cpu().numpy()
586
+
587
+ outputs = []
588
+ start_idx = 0
589
+ for i, num_tokens in enumerate(tokens_per_expert):
590
+ end_idx = start_idx + num_tokens
591
+ if num_tokens == 0:
592
+ continue
593
+ expert = self.experts[i + self.ep_rank * self.experts_per_rank]
594
+ tokens_for_this_expert = sorted_tokens[start_idx:end_idx]
595
+ expert_out = expert(tokens_for_this_expert)
596
+ outputs.append(expert_out)
597
+ start_idx = end_idx
598
+
599
+ outs = torch.cat(outputs,
600
+ dim=0) if len(outputs) else sorted_tokens.new_empty(0)
601
+ if self.ep_size > 1:
602
+ new_x = torch.empty_like(outs)
603
+ new_x[gatherd_idxs] = outs
604
+ gathered_tokens = new_x.new_empty(*sorted_tokens_shape)
605
+ dist.all_to_all(
606
+ list(gathered_tokens.split(input_split_sizes)),
607
+ list(new_x.split(output_splits)),
608
+ )
609
+ outs = gathered_tokens
610
+
611
+ new_x = torch.empty_like(outs)
612
+ new_x[idxs] = outs
613
+ final_out = (new_x.view(
614
+ *topk_ids.shape, -1).type(topk_weight.dtype).mul_(
615
+ topk_weight.unsqueeze(dim=-1)).sum(dim=1).type(new_x.dtype))
616
+ return final_out
617
+
618
+
619
+ # Copied from transformers.models.llama.modeling_llama.repeat_kv
620
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
621
+ """
622
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
623
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
624
+ """
625
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
626
+ if n_rep == 1:
627
+ return hidden_states
628
+ hidden_states = hidden_states[:, :,
629
+ None, :, :].expand(batch,
630
+ num_key_value_heads,
631
+ n_rep, slen, head_dim)
632
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen,
633
+ head_dim)
634
+
635
+
636
+ # Copied from transformers.models.llama.modeling_llama.LlamaAttention with Llama->DeepseekV3
637
+ class DeepseekV3Attention(nn.Module):
638
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
639
+
640
+ def __init__(self,
641
+ config: DeepseekV3Config,
642
+ layer_idx: Optional[int] = None):
643
+ super().__init__()
644
+ self.config = config
645
+ self.layer_idx = layer_idx
646
+ if layer_idx is None:
647
+ logger.warning_once(
648
+ f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
649
+ "to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
650
+ "when creating this class.")
651
+
652
+ self.attention_dropout = config.attention_dropout
653
+ self.hidden_size = config.hidden_size
654
+ self.num_heads = config.num_attention_heads
655
+
656
+ self.max_position_embeddings = config.max_position_embeddings
657
+ self.rope_theta = config.rope_theta
658
+ self.q_lora_rank = config.q_lora_rank
659
+ self.qk_rope_head_dim = config.qk_rope_head_dim
660
+ self.kv_lora_rank = config.kv_lora_rank
661
+ self.v_head_dim = config.v_head_dim
662
+ self.qk_nope_head_dim = config.qk_nope_head_dim
663
+ self.q_head_dim = config.qk_nope_head_dim + config.qk_rope_head_dim
664
+
665
+ self.is_causal = True
666
+
667
+ if self.q_lora_rank is None:
668
+ self.q_proj = nn.Linear(self.hidden_size,
669
+ self.num_heads * self.q_head_dim,
670
+ bias=False)
671
+ else:
672
+ self.q_a_proj = nn.Linear(self.hidden_size,
673
+ config.q_lora_rank,
674
+ bias=config.attention_bias)
675
+ self.q_a_layernorm = DeepseekV3RMSNorm(config.q_lora_rank)
676
+ self.q_b_proj = nn.Linear(config.q_lora_rank,
677
+ self.num_heads * self.q_head_dim,
678
+ bias=False)
679
+
680
+ self.kv_a_proj_with_mqa = nn.Linear(
681
+ self.hidden_size,
682
+ config.kv_lora_rank + config.qk_rope_head_dim,
683
+ bias=config.attention_bias,
684
+ )
685
+ self.kv_a_layernorm = DeepseekV3RMSNorm(config.kv_lora_rank)
686
+ self.kv_b_proj = nn.Linear(
687
+ config.kv_lora_rank,
688
+ self.num_heads *
689
+ (self.q_head_dim - self.qk_rope_head_dim + self.v_head_dim),
690
+ bias=False,
691
+ )
692
+
693
+ self.o_proj = nn.Linear(
694
+ self.num_heads * self.v_head_dim,
695
+ self.hidden_size,
696
+ bias=config.attention_bias,
697
+ )
698
+ self._init_rope()
699
+
700
+ self.softmax_scale = self.q_head_dim**(-0.5)
701
+ if self.config.rope_scaling is not None:
702
+ mscale_all_dim = self.config.rope_scaling.get("mscale_all_dim", 0)
703
+ scaling_factor = self.config.rope_scaling["factor"]
704
+ if mscale_all_dim:
705
+ mscale = yarn_get_mscale(scaling_factor, mscale_all_dim)
706
+ self.softmax_scale = self.softmax_scale * mscale * mscale
707
+
708
+ def _init_rope(self):
709
+ if self.config.rope_scaling is None:
710
+ self.rotary_emb = DeepseekV3RotaryEmbedding(
711
+ self.qk_rope_head_dim,
712
+ max_position_embeddings=self.max_position_embeddings,
713
+ base=self.rope_theta,
714
+ )
715
+ else:
716
+ scaling_type = self.config.rope_scaling["type"]
717
+ scaling_factor = self.config.rope_scaling["factor"]
718
+ if scaling_type == "linear":
719
+ self.rotary_emb = DeepseekV3LinearScalingRotaryEmbedding(
720
+ self.qk_rope_head_dim,
721
+ max_position_embeddings=self.max_position_embeddings,
722
+ scaling_factor=scaling_factor,
723
+ base=self.rope_theta,
724
+ )
725
+ elif scaling_type == "dynamic":
726
+ self.rotary_emb = DeepseekV3DynamicNTKScalingRotaryEmbedding(
727
+ self.qk_rope_head_dim,
728
+ max_position_embeddings=self.max_position_embeddings,
729
+ scaling_factor=scaling_factor,
730
+ base=self.rope_theta,
731
+ )
732
+ elif scaling_type == "yarn":
733
+ kwargs = {
734
+ key: self.config.rope_scaling[key]
735
+ for key in [
736
+ "original_max_position_embeddings",
737
+ "beta_fast",
738
+ "beta_slow",
739
+ "mscale",
740
+ "mscale_all_dim",
741
+ ] if key in self.config.rope_scaling
742
+ }
743
+ self.rotary_emb = DeepseekV3YarnRotaryEmbedding(
744
+ self.qk_rope_head_dim,
745
+ max_position_embeddings=self.max_position_embeddings,
746
+ scaling_factor=scaling_factor,
747
+ base=self.rope_theta,
748
+ **kwargs,
749
+ )
750
+ else:
751
+ raise ValueError(f"Unknown RoPE scaling type {scaling_type}")
752
+
753
+ def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
754
+ return (tensor.view(bsz, seq_len, self.num_heads,
755
+ self.v_head_dim).transpose(1, 2).contiguous())
756
+
757
+ def forward(
758
+ self,
759
+ hidden_states: torch.Tensor,
760
+ attention_mask: Optional[torch.Tensor] = None,
761
+ position_ids: Optional[torch.LongTensor] = None,
762
+ past_key_value: Optional[Cache] = None,
763
+ output_attentions: bool = False,
764
+ use_cache: bool = False,
765
+ **kwargs,
766
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor],
767
+ Optional[Tuple[torch.Tensor]]]:
768
+ if "padding_mask" in kwargs:
769
+ warnings.warn(
770
+ "Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`"
771
+ )
772
+ bsz, q_len, _ = hidden_states.size()
773
+
774
+ if self.q_lora_rank is None:
775
+ q = self.q_proj(hidden_states)
776
+ else:
777
+ q = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
778
+ q = q.view(bsz, q_len, self.num_heads, self.q_head_dim).transpose(1, 2)
779
+ q_nope, q_pe = torch.split(
780
+ q, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
781
+
782
+ compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
783
+ compressed_kv, k_pe = torch.split(
784
+ compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
785
+ k_pe = k_pe.view(bsz, q_len, 1, self.qk_rope_head_dim).transpose(1, 2)
786
+ kv = (self.kv_b_proj(self.kv_a_layernorm(compressed_kv)).view(
787
+ bsz, q_len, self.num_heads,
788
+ self.qk_nope_head_dim + self.v_head_dim).transpose(1, 2))
789
+
790
+ k_nope, value_states = torch.split(
791
+ kv, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
792
+ kv_seq_len = value_states.shape[-2]
793
+ if past_key_value is not None:
794
+ if self.layer_idx is None:
795
+ raise ValueError(
796
+ f"The cache structure has changed since version v4.36. If you are using {self.__class__.__name__} "
797
+ "for auto-regressive decoding with k/v caching, please make sure to initialize the attention class "
798
+ "with a layer index.")
799
+ kv_seq_len += get_usable_length(past_key_value, kv_seq_len,
800
+ self.layer_idx)
801
+ cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)
802
+
803
+ q_pe, k_pe = apply_rotary_pos_emb(q_pe, k_pe, cos, sin, position_ids)
804
+
805
+ query_states = k_pe.new_empty(bsz, self.num_heads, q_len,
806
+ self.q_head_dim)
807
+ query_states[:, :, :, :self.qk_nope_head_dim] = q_nope
808
+ query_states[:, :, :, self.qk_nope_head_dim:] = q_pe
809
+
810
+ key_states = k_pe.new_empty(bsz, self.num_heads, q_len,
811
+ self.q_head_dim)
812
+ key_states[:, :, :, :self.qk_nope_head_dim] = k_nope
813
+ key_states[:, :, :, self.qk_nope_head_dim:] = k_pe
814
+ if past_key_value is not None:
815
+ cache_kwargs = {"sin": sin, "cos": cos} # Specific to RoPE models
816
+ key_states, value_states = past_key_value.update(
817
+ key_states, value_states, self.layer_idx, cache_kwargs)
818
+
819
+ attn_weights = (
820
+ torch.matmul(query_states, key_states.transpose(2, 3)) *
821
+ self.softmax_scale)
822
+
823
+ if attn_weights.size() != (bsz, self.num_heads, q_len, kv_seq_len):
824
+ raise ValueError(
825
+ f"Attention weights should be of size {(bsz, self.num_heads, q_len, kv_seq_len)}, but is"
826
+ f" {attn_weights.size()}")
827
+ assert attention_mask is not None
828
+ if attention_mask is not None:
829
+ if attention_mask.size() != (bsz, 1, q_len, kv_seq_len):
830
+ raise ValueError(
831
+ f"Attention mask should be of size {(bsz, 1, q_len, kv_seq_len)}, but is {attention_mask.size()}"
832
+ )
833
+ attn_weights = attn_weights + attention_mask
834
+
835
+ # upcast attention to fp32
836
+ attn_weights = nn.functional.softmax(attn_weights,
837
+ dim=-1,
838
+ dtype=torch.float32).to(
839
+ query_states.dtype)
840
+ attn_weights = nn.functional.dropout(attn_weights,
841
+ p=self.attention_dropout,
842
+ training=self.training)
843
+ attn_output = torch.matmul(attn_weights, value_states)
844
+
845
+ if attn_output.size() != (bsz, self.num_heads, q_len, self.v_head_dim):
846
+ raise ValueError(
847
+ f"`attn_output` should be of size {(bsz, self.num_heads, q_len, self.v_head_dim)}, but is"
848
+ f" {attn_output.size()}")
849
+
850
+ attn_output = attn_output.transpose(1, 2).contiguous()
851
+
852
+ attn_output = attn_output.reshape(bsz, q_len,
853
+ self.num_heads * self.v_head_dim)
854
+
855
+ attn_output = self.o_proj(attn_output)
856
+
857
+ if not output_attentions:
858
+ attn_weights = None
859
+
860
+ return attn_output, attn_weights, past_key_value
861
+
862
+
863
+ # Copied from transformers.models.llama.modeling_llama.LlamaFlashAttention2 with Llama->DeepseekV3
864
+ class DeepseekV3FlashAttention2(DeepseekV3Attention):
865
+ """
866
+ DeepseekV3 flash attention module. This module inherits from `DeepseekV3Attention` as the weights of the module stays
867
+ untouched. The only required change would be on the forward pass where it needs to correctly call the public API of
868
+ flash attention and deal with padding tokens in case the input contains any of them.
869
+ """
870
+
871
+ def __init__(self, *args, **kwargs):
872
+ super().__init__(*args, **kwargs)
873
+
874
+ # TODO: Should be removed once Flash Attention for RoCm is bumped to 2.1.
875
+ # flash_attn<2.1 generates top-left aligned causal mask, while what is needed here is bottom-right alignment, that was made default for flash_attn>=2.1. This attribute is used to handle this difference. Reference: https://github.com/Dao-AILab/flash-attention/releases/tag/v2.1.0.
876
+ # Beware that with flash_attn<2.1, using q_seqlen != k_seqlen (except for the case q_seqlen == 1) produces a wrong mask (top-left).
877
+ self._flash_attn_uses_top_left_mask = not is_flash_attn_greater_or_equal_2_10(
878
+ )
879
+
880
+ def forward(
881
+ self,
882
+ hidden_states: torch.Tensor,
883
+ attention_mask: Optional[torch.LongTensor] = None,
884
+ position_ids: Optional[torch.LongTensor] = None,
885
+ past_key_value: Optional[Cache] = None,
886
+ output_attentions: bool = False,
887
+ use_cache: bool = False,
888
+ **kwargs,
889
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor],
890
+ Optional[Tuple[torch.Tensor]]]:
891
+ # DeepseekV3FlashAttention2 attention does not support output_attentions
892
+ if "padding_mask" in kwargs:
893
+ warnings.warn(
894
+ "Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`"
895
+ )
896
+
897
+ # overwrite attention_mask with padding_mask
898
+ attention_mask = kwargs.pop("padding_mask")
899
+
900
+ output_attentions = False
901
+
902
+ bsz, q_len, _ = hidden_states.size()
903
+
904
+ if self.q_lora_rank is None:
905
+ q = self.q_proj(hidden_states)
906
+ else:
907
+ q = self.q_b_proj(self.q_a_layernorm(self.q_a_proj(hidden_states)))
908
+ q = q.view(bsz, q_len, self.num_heads, self.q_head_dim).transpose(1, 2)
909
+ q_nope, q_pe = torch.split(
910
+ q, [self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
911
+
912
+ # Flash attention requires the input to have the shape
913
+ # batch_size x seq_length x head_dim x hidden_dim
914
+ # therefore we just need to keep the original shape
915
+ compressed_kv = self.kv_a_proj_with_mqa(hidden_states)
916
+ compressed_kv, k_pe = torch.split(
917
+ compressed_kv, [self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
918
+ k_pe = k_pe.view(bsz, q_len, 1, self.qk_rope_head_dim).transpose(1, 2)
919
+ kv = (self.kv_b_proj(self.kv_a_layernorm(compressed_kv)).view(
920
+ bsz, q_len, self.num_heads,
921
+ self.qk_nope_head_dim + self.v_head_dim).transpose(1, 2))
922
+
923
+ k_nope, value_states = torch.split(
924
+ kv, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
925
+ kv_seq_len = value_states.shape[-2]
926
+
927
+ kv_seq_len = value_states.shape[-2]
928
+ if past_key_value is not None:
929
+ kv_seq_len += get_usable_length(past_key_value, kv_seq_len,
930
+ self.layer_idx)
931
+
932
+ cos, sin = self.rotary_emb(value_states, seq_len=kv_seq_len)
933
+ q_pe, k_pe = apply_rotary_pos_emb(q_pe, k_pe, cos, sin, position_ids)
934
+
935
+ query_states = k_pe.new_empty(bsz, self.num_heads, q_len,
936
+ self.q_head_dim)
937
+ query_states[:, :, :, :self.qk_nope_head_dim] = q_nope
938
+ query_states[:, :, :, self.qk_nope_head_dim:] = q_pe
939
+
940
+ key_states = k_pe.new_empty(bsz, self.num_heads, q_len,
941
+ self.q_head_dim)
942
+ key_states[:, :, :, :self.qk_nope_head_dim] = k_nope
943
+ key_states[:, :, :, self.qk_nope_head_dim:] = k_pe
944
+
945
+ if self.q_head_dim != self.v_head_dim:
946
+ value_states = F.pad(value_states,
947
+ [0, self.q_head_dim - self.v_head_dim])
948
+
949
+ if past_key_value is not None:
950
+ cache_kwargs = {"sin": sin, "cos": cos} # Specific to RoPE models
951
+ key_states, value_states = past_key_value.update(
952
+ key_states, value_states, self.layer_idx, cache_kwargs)
953
+
954
+ # TODO: These transpose are quite inefficient but Flash Attention requires the layout [batch_size, sequence_length, num_heads, head_dim]. We would need to refactor the KV cache
955
+ # to be able to avoid many of these transpose/reshape/view.
956
+ query_states = query_states.transpose(1, 2)
957
+ key_states = key_states.transpose(1, 2)
958
+ value_states = value_states.transpose(1, 2)
959
+
960
+ dropout_rate = self.attention_dropout if self.training else 0.0
961
+
962
+ # In PEFT, usually we cast the layer norms in float32 for training stability reasons
963
+ # therefore the input hidden states gets silently casted in float32. Hence, we need
964
+ # cast them back in the correct dtype just to be sure everything works as expected.
965
+ # This might slowdown training & inference so it is recommended to not cast the LayerNorms
966
+ # in fp32. (DeepseekV3RMSNorm handles it correctly)
967
+
968
+ input_dtype = query_states.dtype
969
+ if input_dtype == torch.float32:
970
+ # Handle the case where the model is quantized
971
+ if hasattr(self.config, "_pre_quantization_dtype"):
972
+ target_dtype = self.config._pre_quantization_dtype
973
+ elif torch.is_autocast_enabled():
974
+ target_dtype = torch.get_autocast_gpu_dtype()
975
+ else:
976
+ target_dtype = (self.q_proj.weight.dtype if self.q_lora_rank
977
+ is None else self.q_a_proj.weight.dtype)
978
+
979
+ logger.warning_once(
980
+ f"The input hidden states seems to be silently casted in float32, this might be related to"
981
+ f" the fact you have upcasted embedding or layer norm layers in float32. We will cast back the input in"
982
+ f" {target_dtype}.")
983
+
984
+ query_states = query_states.to(target_dtype)
985
+ key_states = key_states.to(target_dtype)
986
+ value_states = value_states.to(target_dtype)
987
+
988
+ attn_output = self._flash_attention_forward(
989
+ query_states,
990
+ key_states,
991
+ value_states,
992
+ attention_mask,
993
+ q_len,
994
+ dropout=dropout_rate,
995
+ softmax_scale=self.softmax_scale,
996
+ )
997
+ if self.q_head_dim != self.v_head_dim:
998
+ attn_output = attn_output[:, :, :, :self.v_head_dim]
999
+
1000
+ attn_output = attn_output.reshape(bsz, q_len, self.num_heads *
1001
+ self.v_head_dim).contiguous()
1002
+ attn_output = self.o_proj(attn_output)
1003
+
1004
+ if not output_attentions:
1005
+ attn_weights = None
1006
+
1007
+ return attn_output, attn_weights, past_key_value
1008
+
1009
+ def _flash_attention_forward(
1010
+ self,
1011
+ query_states,
1012
+ key_states,
1013
+ value_states,
1014
+ attention_mask,
1015
+ query_length,
1016
+ dropout=0.0,
1017
+ softmax_scale=None,
1018
+ ):
1019
+ """
1020
+ Calls the forward method of Flash Attention - if the input hidden states contain at least one padding token
1021
+ first unpad the input, then computes the attention scores and pad the final attention scores.
1022
+
1023
+ Args:
1024
+ query_states (`torch.Tensor`):
1025
+ Input query states to be passed to Flash Attention API
1026
+ key_states (`torch.Tensor`):
1027
+ Input key states to be passed to Flash Attention API
1028
+ value_states (`torch.Tensor`):
1029
+ Input value states to be passed to Flash Attention API
1030
+ attention_mask (`torch.Tensor`):
1031
+ The padding mask - corresponds to a tensor of size `(batch_size, seq_len)` where 0 stands for the
1032
+ position of padding tokens and 1 for the position of non-padding tokens.
1033
+ dropout (`int`, *optional*):
1034
+ Attention dropout
1035
+ softmax_scale (`float`, *optional*):
1036
+ The scaling of QK^T before applying softmax. Default to 1 / sqrt(head_dim)
1037
+ """
1038
+ if not self._flash_attn_uses_top_left_mask:
1039
+ causal = self.is_causal
1040
+ else:
1041
+ # TODO: Remove the `query_length != 1` check once Flash Attention for RoCm is bumped to 2.1. For details, please see the comment in DeepseekV3FlashAttention2 __init__.
1042
+ causal = self.is_causal and query_length != 1
1043
+
1044
+ # Contains at least one padding token in the sequence
1045
+ if attention_mask is not None:
1046
+ batch_size = query_states.shape[0]
1047
+ (
1048
+ query_states,
1049
+ key_states,
1050
+ value_states,
1051
+ indices_q,
1052
+ cu_seq_lens,
1053
+ max_seq_lens,
1054
+ ) = self._upad_input(query_states, key_states, value_states,
1055
+ attention_mask, query_length)
1056
+
1057
+ cu_seqlens_q, cu_seqlens_k = cu_seq_lens
1058
+ max_seqlen_in_batch_q, max_seqlen_in_batch_k = max_seq_lens
1059
+
1060
+ attn_output_unpad = flash_attn_varlen_func(
1061
+ query_states,
1062
+ key_states,
1063
+ value_states,
1064
+ cu_seqlens_q=cu_seqlens_q,
1065
+ cu_seqlens_k=cu_seqlens_k,
1066
+ max_seqlen_q=max_seqlen_in_batch_q,
1067
+ max_seqlen_k=max_seqlen_in_batch_k,
1068
+ dropout_p=dropout,
1069
+ softmax_scale=softmax_scale,
1070
+ causal=causal,
1071
+ )
1072
+
1073
+ attn_output = pad_input(attn_output_unpad, indices_q, batch_size,
1074
+ query_length)
1075
+ else:
1076
+ attn_output = flash_attn_func(
1077
+ query_states,
1078
+ key_states,
1079
+ value_states,
1080
+ dropout,
1081
+ softmax_scale=softmax_scale,
1082
+ causal=causal,
1083
+ )
1084
+
1085
+ return attn_output
1086
+
1087
+ def _upad_input(self, query_layer, key_layer, value_layer, attention_mask,
1088
+ query_length):
1089
+ indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(
1090
+ attention_mask)
1091
+ batch_size, kv_seq_len, num_key_value_heads, head_dim = key_layer.shape
1092
+
1093
+ key_layer = index_first_axis(
1094
+ key_layer.reshape(batch_size * kv_seq_len, num_key_value_heads,
1095
+ head_dim),
1096
+ indices_k,
1097
+ )
1098
+ value_layer = index_first_axis(
1099
+ value_layer.reshape(batch_size * kv_seq_len, num_key_value_heads,
1100
+ head_dim),
1101
+ indices_k,
1102
+ )
1103
+ if query_length == kv_seq_len:
1104
+ query_layer = index_first_axis(
1105
+ query_layer.reshape(batch_size * kv_seq_len, self.num_heads,
1106
+ head_dim),
1107
+ indices_k,
1108
+ )
1109
+ cu_seqlens_q = cu_seqlens_k
1110
+ max_seqlen_in_batch_q = max_seqlen_in_batch_k
1111
+ indices_q = indices_k
1112
+ elif query_length == 1:
1113
+ max_seqlen_in_batch_q = 1
1114
+ cu_seqlens_q = torch.arange(
1115
+ batch_size + 1, dtype=torch.int32, device=query_layer.device
1116
+ ) # There is a memcpy here, that is very bad.
1117
+ indices_q = cu_seqlens_q[:-1]
1118
+ query_layer = query_layer.squeeze(1)
1119
+ else:
1120
+ # The -q_len: slice assumes left padding.
1121
+ attention_mask = attention_mask[:, -query_length:]
1122
+ query_layer, indices_q, cu_seqlens_q, max_seqlen_in_batch_q = unpad_input(
1123
+ query_layer, attention_mask)
1124
+
1125
+ return (
1126
+ query_layer,
1127
+ key_layer,
1128
+ value_layer,
1129
+ indices_q,
1130
+ (cu_seqlens_q, cu_seqlens_k),
1131
+ (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
1132
+ )
1133
+
1134
+
1135
+ ATTENTION_CLASSES = {
1136
+ "eager": DeepseekV3Attention,
1137
+ "flash_attention_2": DeepseekV3FlashAttention2,
1138
+ }
1139
+
1140
+
1141
+ class DeepseekV3DecoderLayer(nn.Module):
1142
+
1143
+ def __init__(self, config: DeepseekV3Config, layer_idx: int):
1144
+ super().__init__()
1145
+ self.hidden_size = config.hidden_size
1146
+
1147
+ self.self_attn = ATTENTION_CLASSES[config._attn_implementation](
1148
+ config=config, layer_idx=layer_idx)
1149
+
1150
+ self.mlp = (DeepseekV3MoE(config) if
1151
+ (config.n_routed_experts is not None
1152
+ and layer_idx >= config.first_k_dense_replace
1153
+ and layer_idx % config.moe_layer_freq == 0) else
1154
+ DeepseekV3MLP(config))
1155
+ self.input_layernorm = DeepseekV3RMSNorm(config.hidden_size,
1156
+ eps=config.rms_norm_eps)
1157
+ self.post_attention_layernorm = DeepseekV3RMSNorm(
1158
+ config.hidden_size, eps=config.rms_norm_eps)
1159
+
1160
+ def forward(
1161
+ self,
1162
+ hidden_states: torch.Tensor,
1163
+ attention_mask: Optional[torch.Tensor] = None,
1164
+ position_ids: Optional[torch.LongTensor] = None,
1165
+ past_key_value: Optional[Tuple[torch.Tensor]] = None,
1166
+ output_attentions: Optional[bool] = False,
1167
+ use_cache: Optional[bool] = False,
1168
+ **kwargs,
1169
+ ) -> Tuple[torch.FloatTensor, Optional[Tuple[torch.FloatTensor,
1170
+ torch.FloatTensor]]]:
1171
+ """
1172
+ Args:
1173
+ hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
1174
+ attention_mask (`torch.FloatTensor`, *optional*):
1175
+ attention mask of size `(batch_size, sequence_length)` if flash attention is used or `(batch_size, 1,
1176
+ query_sequence_length, key_sequence_length)` if default attention is used.
1177
+ output_attentions (`bool`, *optional*):
1178
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under
1179
+ returned tensors for more detail.
1180
+ use_cache (`bool`, *optional*):
1181
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
1182
+ (see `past_key_values`).
1183
+ past_key_value (`Tuple(torch.FloatTensor)`, *optional*): cached past key and value projection states
1184
+ """
1185
+ if "padding_mask" in kwargs:
1186
+ warnings.warn(
1187
+ "Passing `padding_mask` is deprecated and will be removed in v4.37. Please make sure use `attention_mask` instead.`"
1188
+ )
1189
+ residual = hidden_states
1190
+
1191
+ hidden_states = self.input_layernorm(hidden_states)
1192
+
1193
+ # Self Attention
1194
+ hidden_states, self_attn_weights, present_key_value = self.self_attn(
1195
+ hidden_states=hidden_states,
1196
+ attention_mask=attention_mask,
1197
+ position_ids=position_ids,
1198
+ past_key_value=past_key_value,
1199
+ output_attentions=output_attentions,
1200
+ use_cache=use_cache,
1201
+ **kwargs,
1202
+ )
1203
+ hidden_states = residual + hidden_states
1204
+
1205
+ # Fully Connected
1206
+ residual = hidden_states
1207
+ hidden_states = self.post_attention_layernorm(hidden_states)
1208
+ hidden_states = self.mlp(hidden_states)
1209
+ hidden_states = residual + hidden_states
1210
+
1211
+ outputs = (hidden_states, )
1212
+
1213
+ if output_attentions:
1214
+ outputs += (self_attn_weights, )
1215
+
1216
+ if use_cache:
1217
+ outputs += (present_key_value, )
1218
+
1219
+ return outputs
1220
+
1221
+
1222
+ DeepseekV3_START_DOCSTRING = r"""
1223
+ This model inherits from [`PreTrainedModel`]. Check the superclass documentation for the generic methods the
1224
+ library implements for all its model (such as downloading or saving, resizing the input embeddings, pruning heads
1225
+ etc.)
1226
+
1227
+ This model is also a PyTorch [torch.nn.Module](https://pytorch.org/docs/stable/nn.html#torch.nn.Module) subclass.
1228
+ Use it as a regular PyTorch Module and refer to the PyTorch documentation for all matter related to general usage
1229
+ and behavior.
1230
+
1231
+ Parameters:
1232
+ config ([`DeepseekV3Config`]):
1233
+ Model configuration class with all the parameters of the model. Initializing with a config file does not
1234
+ load the weights associated with the model, only the configuration. Check out the
1235
+ [`~PreTrainedModel.from_pretrained`] method to load the model weights.
1236
+ """
1237
+
1238
+
1239
+ @add_start_docstrings(
1240
+ "The bare DeepseekV3 Model outputting raw hidden-states without any specific head on top.",
1241
+ DeepseekV3_START_DOCSTRING,
1242
+ )
1243
+ class DeepseekV3PreTrainedModel(PreTrainedModel):
1244
+ config_class = DeepseekV3Config
1245
+ base_model_prefix = "model"
1246
+ supports_gradient_checkpointing = True
1247
+ _no_split_modules = ["DeepseekV3DecoderLayer"]
1248
+ _skip_keys_device_placement = "past_key_values"
1249
+ _supports_flash_attn_2 = True
1250
+ _supports_cache_class = True
1251
+
1252
+ def _init_weights(self, module):
1253
+ std = self.config.initializer_range
1254
+ if isinstance(module, nn.Linear):
1255
+ module.weight.data.normal_(mean=0.0, std=std)
1256
+ if module.bias is not None:
1257
+ module.bias.data.zero_()
1258
+ elif isinstance(module, nn.Embedding):
1259
+ module.weight.data.normal_(mean=0.0, std=std)
1260
+ if module.padding_idx is not None:
1261
+ module.weight.data[module.padding_idx].zero_()
1262
+
1263
+
1264
+ DeepseekV3_INPUTS_DOCSTRING = r"""
1265
+ Args:
1266
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
1267
+ Indices of input sequence tokens in the vocabulary. Padding will be ignored by default should you provide
1268
+ it.
1269
+
1270
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
1271
+ [`PreTrainedTokenizer.__call__`] for details.
1272
+
1273
+ [What are input IDs?](../glossary#input-ids)
1274
+ attention_mask (`torch.Tensor` of shape `(batch_size, sequence_length)`, *optional*):
1275
+ Mask to avoid performing attention on padding token indices. Mask values selected in `[0, 1]`:
1276
+
1277
+ - 1 for tokens that are **not masked**,
1278
+ - 0 for tokens that are **masked**.
1279
+
1280
+ [What are attention masks?](../glossary#attention-mask)
1281
+
1282
+ Indices can be obtained using [`AutoTokenizer`]. See [`PreTrainedTokenizer.encode`] and
1283
+ [`PreTrainedTokenizer.__call__`] for details.
1284
+
1285
+ If `past_key_values` is used, optionally only the last `input_ids` have to be input (see
1286
+ `past_key_values`).
1287
+
1288
+ If you want to change padding behavior, you should read [`modeling_opt._prepare_decoder_attention_mask`]
1289
+ and modify to your needs. See diagram 1 in [the paper](https://arxiv.org/abs/1910.13461) for more
1290
+ information on the default strategy.
1291
+
1292
+ - 1 indicates the head is **not masked**,
1293
+ - 0 indicates the head is **masked**.
1294
+ position_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1295
+ Indices of positions of each input sequence tokens in the position embeddings. Selected in the range `[0,
1296
+ config.n_positions - 1]`.
1297
+
1298
+ [What are position IDs?](../glossary#position-ids)
1299
+ past_key_values (`Cache` or `tuple(tuple(torch.FloatTensor))`, *optional*):
1300
+ Pre-computed hidden-states (key and values in the self-attention blocks and in the cross-attention
1301
+ blocks) that can be used to speed up sequential decoding. This typically consists in the `past_key_values`
1302
+ returned by the model at a previous stage of decoding, when `use_cache=True` or `config.use_cache=True`.
1303
+
1304
+ Two formats are allowed:
1305
+ - a [`~cache_utils.Cache`] instance;
1306
+ - Tuple of `tuple(torch.FloatTensor)` of length `config.n_layers`, with each tuple having 2 tensors of
1307
+ shape `(batch_size, num_heads, sequence_length, embed_size_per_head)`). This is also known as the legacy
1308
+ cache format.
1309
+
1310
+ The model will output the same cache format that is fed as input. If no `past_key_values` are passed, the
1311
+ legacy cache format will be returned.
1312
+
1313
+ If `past_key_values` are used, the user can optionally input only the last `input_ids` (those that don't
1314
+ have their past key value states given to this model) of shape `(batch_size, 1)` instead of all `input_ids`
1315
+ of shape `(batch_size, sequence_length)`.
1316
+ inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`, *optional*):
1317
+ Optionally, instead of passing `input_ids` you can choose to directly pass an embedded representation. This
1318
+ is useful if you want more control over how to convert `input_ids` indices into associated vectors than the
1319
+ model's internal embedding lookup matrix.
1320
+ use_cache (`bool`, *optional*):
1321
+ If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding (see
1322
+ `past_key_values`).
1323
+ output_attentions (`bool`, *optional*):
1324
+ Whether or not to return the attentions tensors of all attention layers. See `attentions` under returned
1325
+ tensors for more detail.
1326
+ output_hidden_states (`bool`, *optional*):
1327
+ Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors for
1328
+ more detail.
1329
+ return_dict (`bool`, *optional*):
1330
+ Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
1331
+ """
1332
+
1333
+
1334
+ @add_start_docstrings(
1335
+ "The bare DeepseekV3 Model outputting raw hidden-states without any specific head on top.",
1336
+ DeepseekV3_START_DOCSTRING,
1337
+ )
1338
+ class DeepseekV3Model(DeepseekV3PreTrainedModel):
1339
+ """
1340
+ Transformer decoder consisting of *config.num_hidden_layers* layers. Each layer is a [`DeepseekV3DecoderLayer`]
1341
+
1342
+ Args:
1343
+ config: DeepseekV3Config
1344
+ """
1345
+
1346
+ def __init__(self, config: DeepseekV3Config):
1347
+ super().__init__(config)
1348
+ self.padding_idx = config.pad_token_id
1349
+ self.vocab_size = config.vocab_size
1350
+
1351
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size,
1352
+ self.padding_idx)
1353
+ self.layers = nn.ModuleList([
1354
+ DeepseekV3DecoderLayer(config, layer_idx)
1355
+ for layer_idx in range(config.num_hidden_layers)
1356
+ ])
1357
+ self._use_flash_attention_2 = config._attn_implementation == "flash_attention_2"
1358
+ self.norm = DeepseekV3RMSNorm(config.hidden_size,
1359
+ eps=config.rms_norm_eps)
1360
+
1361
+ self.gradient_checkpointing = False
1362
+ # Initialize weights and apply final processing
1363
+ self.post_init()
1364
+
1365
+ def get_input_embeddings(self):
1366
+ return self.embed_tokens
1367
+
1368
+ def set_input_embeddings(self, value):
1369
+ self.embed_tokens = value
1370
+
1371
+ @add_start_docstrings_to_model_forward(DeepseekV3_INPUTS_DOCSTRING)
1372
+ def forward(
1373
+ self,
1374
+ input_ids: torch.LongTensor = None,
1375
+ attention_mask: Optional[torch.Tensor] = None,
1376
+ position_ids: Optional[torch.LongTensor] = None,
1377
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
1378
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1379
+ use_cache: Optional[bool] = None,
1380
+ output_attentions: Optional[bool] = None,
1381
+ output_hidden_states: Optional[bool] = None,
1382
+ return_dict: Optional[bool] = None,
1383
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
1384
+ output_attentions = (output_attentions if output_attentions is not None
1385
+ else self.config.output_attentions)
1386
+ output_hidden_states = (output_hidden_states
1387
+ if output_hidden_states is not None else
1388
+ self.config.output_hidden_states)
1389
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
1390
+
1391
+ return_dict = (return_dict if return_dict is not None else
1392
+ self.config.use_return_dict)
1393
+
1394
+ # retrieve input_ids and inputs_embeds
1395
+ if input_ids is not None and inputs_embeds is not None:
1396
+ raise ValueError(
1397
+ "You cannot specify both input_ids and inputs_embeds at the same time"
1398
+ )
1399
+ elif input_ids is not None:
1400
+ batch_size, seq_length = input_ids.shape[:2]
1401
+ elif inputs_embeds is not None:
1402
+ batch_size, seq_length = inputs_embeds.shape[:2]
1403
+ else:
1404
+ raise ValueError(
1405
+ "You have to specify either input_ids or inputs_embeds")
1406
+
1407
+ past_key_values_length = 0
1408
+ if use_cache:
1409
+ use_legacy_cache = not isinstance(past_key_values, Cache)
1410
+ if use_legacy_cache:
1411
+ past_key_values = DynamicCache.from_legacy_cache(
1412
+ past_key_values)
1413
+ past_key_values_length = get_usable_length(past_key_values,
1414
+ seq_length)
1415
+
1416
+ if position_ids is None:
1417
+ device = input_ids.device if input_ids is not None else inputs_embeds.device
1418
+ position_ids = torch.arange(
1419
+ past_key_values_length,
1420
+ seq_length + past_key_values_length,
1421
+ dtype=torch.long,
1422
+ device=device,
1423
+ )
1424
+ position_ids = position_ids.unsqueeze(0)
1425
+
1426
+ if inputs_embeds is None:
1427
+ inputs_embeds = self.embed_tokens(input_ids)
1428
+
1429
+ if self._use_flash_attention_2:
1430
+ # 2d mask is passed through the layers
1431
+ attention_mask = (attention_mask if
1432
+ (attention_mask is not None
1433
+ and 0 in attention_mask) else None)
1434
+ else:
1435
+ # 4d mask is passed through the layers
1436
+ attention_mask = _prepare_4d_causal_attention_mask(
1437
+ attention_mask,
1438
+ (batch_size, seq_length),
1439
+ inputs_embeds,
1440
+ past_key_values_length,
1441
+ )
1442
+
1443
+ # embed positions
1444
+ hidden_states = inputs_embeds
1445
+
1446
+ # decoder layers
1447
+ all_hidden_states = () if output_hidden_states else None
1448
+ all_self_attns = () if output_attentions else None
1449
+ next_decoder_cache = None
1450
+
1451
+ for decoder_layer in self.layers:
1452
+ if output_hidden_states:
1453
+ all_hidden_states += (hidden_states, )
1454
+
1455
+ layer_outputs = decoder_layer(
1456
+ hidden_states,
1457
+ attention_mask=attention_mask,
1458
+ position_ids=position_ids,
1459
+ past_key_value=past_key_values,
1460
+ output_attentions=output_attentions,
1461
+ use_cache=use_cache,
1462
+ )
1463
+
1464
+ hidden_states = layer_outputs[0]
1465
+
1466
+ if use_cache:
1467
+ next_decoder_cache = layer_outputs[
1468
+ 2 if output_attentions else 1]
1469
+
1470
+ if output_attentions:
1471
+ all_self_attns += (layer_outputs[1], )
1472
+
1473
+ hidden_states = self.norm(hidden_states)
1474
+
1475
+ # add hidden states from the last decoder layer
1476
+ if output_hidden_states:
1477
+ all_hidden_states += (hidden_states, )
1478
+
1479
+ next_cache = None
1480
+ if use_cache:
1481
+ next_cache = (next_decoder_cache.to_legacy_cache()
1482
+ if use_legacy_cache else next_decoder_cache)
1483
+ if not return_dict:
1484
+ return tuple(
1485
+ v for v in
1486
+ [hidden_states, next_cache, all_hidden_states, all_self_attns]
1487
+ if v is not None)
1488
+ return BaseModelOutputWithPast(
1489
+ last_hidden_state=hidden_states,
1490
+ past_key_values=next_cache,
1491
+ hidden_states=all_hidden_states,
1492
+ attentions=all_self_attns,
1493
+ )
1494
+
1495
+
1496
+ class DeepseekV3ForCausalLM(DeepseekV3PreTrainedModel):
1497
+ _tied_weights_keys = ["lm_head.weight"]
1498
+
1499
+ def __init__(self, config):
1500
+ super().__init__(config)
1501
+ self.model = DeepseekV3Model(config)
1502
+ self.vocab_size = config.vocab_size
1503
+ self.lm_head = nn.Linear(config.hidden_size,
1504
+ config.vocab_size,
1505
+ bias=False)
1506
+
1507
+ # Initialize weights and apply final processing
1508
+ self.post_init()
1509
+
1510
+ def get_input_embeddings(self):
1511
+ return self.model.embed_tokens
1512
+
1513
+ def set_input_embeddings(self, value):
1514
+ self.model.embed_tokens = value
1515
+
1516
+ def get_output_embeddings(self):
1517
+ return self.lm_head
1518
+
1519
+ def set_output_embeddings(self, new_embeddings):
1520
+ self.lm_head = new_embeddings
1521
+
1522
+ def set_decoder(self, decoder):
1523
+ self.model = decoder
1524
+
1525
+ def get_decoder(self):
1526
+ return self.model
1527
+
1528
+ @add_start_docstrings_to_model_forward(DeepseekV3_INPUTS_DOCSTRING)
1529
+ @replace_return_docstrings(output_type=CausalLMOutputWithPast,
1530
+ config_class=_CONFIG_FOR_DOC)
1531
+ def forward(
1532
+ self,
1533
+ input_ids: torch.LongTensor = None,
1534
+ attention_mask: Optional[torch.Tensor] = None,
1535
+ position_ids: Optional[torch.LongTensor] = None,
1536
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
1537
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1538
+ labels: Optional[torch.LongTensor] = None,
1539
+ use_cache: Optional[bool] = None,
1540
+ output_attentions: Optional[bool] = None,
1541
+ output_hidden_states: Optional[bool] = None,
1542
+ return_dict: Optional[bool] = None,
1543
+ ) -> Union[Tuple, CausalLMOutputWithPast]:
1544
+ r"""
1545
+ Args:
1546
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1547
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, transformers.,
1548
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
1549
+ (masked), the loss is only computed for the tokens with labels in `[0, transformers., config.vocab_size]`.
1550
+
1551
+ Returns:
1552
+
1553
+ Example:
1554
+
1555
+ ```python
1556
+ >>> from transformers import AutoTokenizer, DeepseekV3ForCausalLM
1557
+
1558
+ >>> model = DeepseekV3ForCausalLM.from_pretrained(PATH_TO_CONVERTED_WEIGHTS)
1559
+ >>> tokenizer = AutoTokenizer.from_pretrained(PATH_TO_CONVERTED_TOKENIZER)
1560
+
1561
+ >>> prompt = "Hey, are you conscious? Can you talk to me?"
1562
+ >>> inputs = tokenizer(prompt, return_tensors="pt")
1563
+
1564
+ >>> # Generate
1565
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
1566
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
1567
+ "Hey, are you conscious? Can you talk to me?\nI'm not conscious, but I can talk to you."
1568
+ ```"""
1569
+ output_attentions = (output_attentions if output_attentions is not None
1570
+ else self.config.output_attentions)
1571
+ output_hidden_states = (output_hidden_states
1572
+ if output_hidden_states is not None else
1573
+ self.config.output_hidden_states)
1574
+ return_dict = (return_dict if return_dict is not None else
1575
+ self.config.use_return_dict)
1576
+
1577
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
1578
+ outputs = self.model(
1579
+ input_ids=input_ids,
1580
+ attention_mask=attention_mask,
1581
+ position_ids=position_ids,
1582
+ past_key_values=past_key_values,
1583
+ inputs_embeds=inputs_embeds,
1584
+ use_cache=use_cache,
1585
+ output_attentions=output_attentions,
1586
+ output_hidden_states=output_hidden_states,
1587
+ return_dict=return_dict,
1588
+ )
1589
+
1590
+ hidden_states = outputs[0]
1591
+ logits = self.lm_head(hidden_states)
1592
+ logits = logits.float()
1593
+
1594
+ loss = None
1595
+ if labels is not None:
1596
+ # Shift so that tokens < n predict n
1597
+ shift_logits = logits[..., :-1, :].contiguous()
1598
+ shift_labels = labels[..., 1:].contiguous()
1599
+ # Flatten the tokens
1600
+ loss_fct = CrossEntropyLoss()
1601
+ shift_logits = shift_logits.view(-1, self.config.vocab_size)
1602
+ shift_labels = shift_labels.view(-1)
1603
+ # Enable model parallelism
1604
+ shift_labels = shift_labels.to(shift_logits.device)
1605
+ loss = loss_fct(shift_logits, shift_labels)
1606
+
1607
+ if not return_dict:
1608
+ output = (logits, ) + outputs[1:]
1609
+ return (loss, ) + output if loss is not None else output
1610
+
1611
+ return CausalLMOutputWithPast(
1612
+ loss=loss,
1613
+ logits=logits,
1614
+ past_key_values=outputs.past_key_values,
1615
+ hidden_states=outputs.hidden_states,
1616
+ attentions=outputs.attentions,
1617
+ )
1618
+
1619
+ def prepare_inputs_for_generation(
1620
+ self,
1621
+ input_ids,
1622
+ past_key_values=None,
1623
+ attention_mask=None,
1624
+ inputs_embeds=None,
1625
+ **kwargs,
1626
+ ):
1627
+ if past_key_values is not None:
1628
+ if isinstance(past_key_values, Cache):
1629
+ cache_length = past_key_values.get_seq_length()
1630
+ # seen_tokens 可能在某些 transformers 版本中不存在,使用 getattr 安全访问
1631
+ past_length = getattr(past_key_values, 'seen_tokens',
1632
+ cache_length)
1633
+ max_cache_length = past_key_values.get_max_length()
1634
+ else:
1635
+ cache_length = past_length = past_key_values[0][0].shape[2]
1636
+ max_cache_length = None
1637
+
1638
+ # Keep only the unprocessed tokens:
1639
+ # 1 - If the length of the attention_mask exceeds the length of input_ids, then we are in a setting where
1640
+ # some of the inputs are exclusively passed as part of the cache (e.g. when passing input_embeds as
1641
+ # input)
1642
+ if (attention_mask is not None
1643
+ and attention_mask.shape[1] > input_ids.shape[1]):
1644
+ input_ids = input_ids[:, -(attention_mask.shape[1] -
1645
+ past_length):]
1646
+ # 2 - If the past_length is smaller than input_ids', then input_ids holds all input tokens. We can discard
1647
+ # input_ids based on the past_length.
1648
+ elif past_length < input_ids.shape[1]:
1649
+ input_ids = input_ids[:, past_length:]
1650
+ # 3 - Otherwise (past_length >= input_ids.shape[1]), let's assume input_ids only has unprocessed tokens.
1651
+
1652
+ # If we are about to go beyond the maximum cache length, we need to crop the input attention mask.
1653
+ if (max_cache_length is not None and attention_mask is not None
1654
+ and cache_length + input_ids.shape[1] > max_cache_length):
1655
+ attention_mask = attention_mask[:, -max_cache_length:]
1656
+
1657
+ position_ids = kwargs.get("position_ids", None)
1658
+ if attention_mask is not None and position_ids is None:
1659
+ # create position_ids on the fly for batch generation
1660
+ position_ids = attention_mask.long().cumsum(-1) - 1
1661
+ position_ids.masked_fill_(attention_mask == 0, 1)
1662
+ if past_key_values:
1663
+ position_ids = position_ids[:, -input_ids.shape[1]:]
1664
+
1665
+ # if `inputs_embeds` are passed, we only want to use them in the 1st generation step
1666
+ if inputs_embeds is not None and past_key_values is None:
1667
+ model_inputs = {"inputs_embeds": inputs_embeds}
1668
+ else:
1669
+ model_inputs = {"input_ids": input_ids}
1670
+
1671
+ model_inputs.update({
1672
+ "position_ids": position_ids,
1673
+ "past_key_values": past_key_values,
1674
+ "use_cache": kwargs.get("use_cache"),
1675
+ "attention_mask": attention_mask,
1676
+ })
1677
+ return model_inputs
1678
+
1679
+ @staticmethod
1680
+ def _reorder_cache(past_key_values, beam_idx):
1681
+ reordered_past = ()
1682
+ for layer_past in past_key_values:
1683
+ reordered_past += (tuple(
1684
+ past_state.index_select(0, beam_idx.to(past_state.device))
1685
+ for past_state in layer_past), )
1686
+ return reordered_past
1687
+
1688
+
1689
+ @add_start_docstrings(
1690
+ """
1691
+ The DeepseekV3 Model transformer with a sequence classification head on top (linear layer).
1692
+
1693
+ [`DeepseekV3ForSequenceClassification`] uses the last token in order to do the classification, as other causal models
1694
+ (e.g. GPT-2) do.
1695
+
1696
+ Since it does classification on the last token, it requires to know the position of the last token. If a
1697
+ `pad_token_id` is defined in the configuration, it finds the last token that is not a padding token in each row. If
1698
+ no `pad_token_id` is defined, it simply takes the last value in each row of the batch. Since it cannot guess the
1699
+ padding tokens when `inputs_embeds` are passed instead of `input_ids`, it does the same (take the last value in
1700
+ each row of the batch).
1701
+ """,
1702
+ DeepseekV3_START_DOCSTRING,
1703
+ )
1704
+ class DeepseekV3ForSequenceClassification(DeepseekV3PreTrainedModel):
1705
+
1706
+ def __init__(self, config):
1707
+ super().__init__(config)
1708
+ self.num_labels = config.num_labels
1709
+ self.model = DeepseekV3Model(config)
1710
+ self.score = nn.Linear(config.hidden_size, self.num_labels, bias=False)
1711
+
1712
+ # Initialize weights and apply final processing
1713
+ self.post_init()
1714
+
1715
+ def get_input_embeddings(self):
1716
+ return self.model.embed_tokens
1717
+
1718
+ def set_input_embeddings(self, value):
1719
+ self.model.embed_tokens = value
1720
+
1721
+ @add_start_docstrings_to_model_forward(DeepseekV3_INPUTS_DOCSTRING)
1722
+ def forward(
1723
+ self,
1724
+ input_ids: torch.LongTensor = None,
1725
+ attention_mask: Optional[torch.Tensor] = None,
1726
+ position_ids: Optional[torch.LongTensor] = None,
1727
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
1728
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1729
+ labels: Optional[torch.LongTensor] = None,
1730
+ use_cache: Optional[bool] = None,
1731
+ output_attentions: Optional[bool] = None,
1732
+ output_hidden_states: Optional[bool] = None,
1733
+ return_dict: Optional[bool] = None,
1734
+ ) -> Union[Tuple, SequenceClassifierOutputWithPast]:
1735
+ r"""
1736
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
1737
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, transformers.,
1738
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
1739
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
1740
+ """
1741
+ return_dict = (return_dict if return_dict is not None else
1742
+ self.config.use_return_dict)
1743
+
1744
+ transformer_outputs = self.model(
1745
+ input_ids,
1746
+ attention_mask=attention_mask,
1747
+ position_ids=position_ids,
1748
+ past_key_values=past_key_values,
1749
+ inputs_embeds=inputs_embeds,
1750
+ use_cache=use_cache,
1751
+ output_attentions=output_attentions,
1752
+ output_hidden_states=output_hidden_states,
1753
+ return_dict=return_dict,
1754
+ )
1755
+ hidden_states = transformer_outputs[0]
1756
+ logits = self.score(hidden_states)
1757
+
1758
+ if input_ids is not None:
1759
+ batch_size = input_ids.shape[0]
1760
+ else:
1761
+ batch_size = inputs_embeds.shape[0]
1762
+
1763
+ if self.config.pad_token_id is None and batch_size != 1:
1764
+ raise ValueError(
1765
+ "Cannot handle batch sizes > 1 if no padding token is defined."
1766
+ )
1767
+ if self.config.pad_token_id is None:
1768
+ sequence_lengths = -1
1769
+ else:
1770
+ if input_ids is not None:
1771
+ sequence_lengths = (torch.eq(
1772
+ input_ids, self.config.pad_token_id).int().argmax(-1) -
1773
+ 1).to(logits.device)
1774
+ else:
1775
+ sequence_lengths = -1
1776
+
1777
+ pooled_logits = logits[torch.arange(batch_size, device=logits.device),
1778
+ sequence_lengths]
1779
+
1780
+ loss = None
1781
+ if labels is not None:
1782
+ labels = labels.to(logits.device)
1783
+ if self.config.problem_type is None:
1784
+ if self.num_labels == 1:
1785
+ self.config.problem_type = "regression"
1786
+ elif self.num_labels > 1 and (labels.dtype == torch.long
1787
+ or labels.dtype == torch.int):
1788
+ self.config.problem_type = "single_label_classification"
1789
+ else:
1790
+ self.config.problem_type = "multi_label_classification"
1791
+
1792
+ if self.config.problem_type == "regression":
1793
+ loss_fct = MSELoss()
1794
+ if self.num_labels == 1:
1795
+ loss = loss_fct(pooled_logits.squeeze(), labels.squeeze())
1796
+ else:
1797
+ loss = loss_fct(pooled_logits, labels)
1798
+ elif self.config.problem_type == "single_label_classification":
1799
+ loss_fct = CrossEntropyLoss()
1800
+ loss = loss_fct(pooled_logits.view(-1, self.num_labels),
1801
+ labels.view(-1))
1802
+ elif self.config.problem_type == "multi_label_classification":
1803
+ loss_fct = BCEWithLogitsLoss()
1804
+ loss = loss_fct(pooled_logits, labels)
1805
+ if not return_dict:
1806
+ output = (pooled_logits, ) + transformer_outputs[1:]
1807
+ return ((loss, ) + output) if loss is not None else output
1808
+
1809
+ return SequenceClassifierOutputWithPast(
1810
+ loss=loss,
1811
+ logits=pooled_logits,
1812
+ past_key_values=transformer_outputs.past_key_values,
1813
+ hidden_states=transformer_outputs.hidden_states,
1814
+ attentions=transformer_outputs.attentions,
1815
+ )
modeling_kimi_k25.py ADDED
@@ -0,0 +1,1294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2025-2026 The Moonshot AI Team, DeepSeek-AI, and HuggingFace Inc. team. All rights reserved.
3
+ #
4
+ # The code is based on llava (llava/modeling_llava.py) and DeepSeek-V3 (DeepSeek-V3/modeling_deepseek.py), but modified for Kimi-K2.5.
5
+ #
6
+ # Licensing Information:
7
+ # - Code derived from llava (llava/modeling_llava.py) and DeepSeek-V3 (DeepSeek-V3/modeling_deepseek.py) is licensed under the Apache License, Version 2.0.
8
+ # - Other parts of the code are licensed under the MIT License.
9
+ #
10
+ # Apache License, Version 2.0:
11
+ # Licensed under the Apache License, Version 2.0 (the "License");
12
+ # you may not use this file except in compliance with the License.
13
+ # You may obtain a copy of the License at
14
+ #
15
+ # http://www.apache.org/licenses/LICENSE-2.0
16
+ #
17
+ # Unless required by applicable law or agreed to in writing, software
18
+ # distributed under the License is distributed on an "AS IS" BASIS,
19
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ # See the License for the specific language governing permissions and
21
+ # limitations under the License.
22
+ #
23
+ # MIT License:
24
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ # of this software and associated documentation files (the "Software"), to deal
26
+ # in the Software without restriction, including without limitation the rights
27
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ # copies of the Software, and to permit persons to whom the Software is
29
+ # furnished to do so, subject to the following conditions:
30
+ #
31
+ # The above copyright notice and this permission notice shall be included in all
32
+ # copies or substantial portions of the Software.
33
+ #
34
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ # SOFTWARE.
41
+ import math
42
+ from collections.abc import Sequence
43
+ from copy import deepcopy
44
+ from typing import Optional
45
+
46
+ import numpy as np
47
+ import torch
48
+ import torch.nn as nn
49
+ import torch.nn.functional as F
50
+ from transformers import activations
51
+
52
+ try:
53
+ from transformers.activations import PytorchGELUTanh
54
+ except ImportError:
55
+ from transformers.activations import GELUTanh
56
+ activations.PytorchGELUTanh = GELUTanh
57
+ PytorchGELUTanh = GELUTanh
58
+ from transformers.activations import PytorchGELUTanh
59
+ from transformers.cache_utils import Cache
60
+ from transformers.configuration_utils import PretrainedConfig
61
+ from transformers.modeling_utils import PreTrainedModel
62
+ from transformers.models.llava.modeling_llava import \
63
+ LlavaCausalLMOutputWithPast
64
+ from transformers.utils import is_flash_attn_2_available
65
+
66
+ from .configuration_kimi_k25 import KimiK25Config
67
+ from .modeling_deepseek import DeepseekV3ForCausalLM
68
+
69
+ # Flash attention imports
70
+ if is_flash_attn_2_available():
71
+ from flash_attn import flash_attn_varlen_func
72
+ else:
73
+ flash_attn_varlen_func = None
74
+
75
+
76
+ def multihead_attention(
77
+ q: torch.Tensor,
78
+ k: torch.Tensor,
79
+ v: torch.Tensor,
80
+ q_cu_seqlens: torch.Tensor | None = None,
81
+ k_cu_seqlens: torch.Tensor | None = None,
82
+ max_seqlen_q: int | None = None,
83
+ max_seqlen_k: int | None = None,
84
+ deterministic: bool = False,
85
+ ):
86
+ """Multi-head attention using flash attention 2.
87
+
88
+ Args:
89
+ q, k, v: tensor of shape (batch_size, seqlen, num_heads, head_dim),
90
+ or (tot_seqlens, num_heads, head_dim) if packing.
91
+ q_cu_seqlens (torch.Tensor): cumulative sequence lengths of q.
92
+ The first element should be 0 and the last element should be q.shape[0].
93
+ k_cu_seqlens (torch.Tensor): cumulative sequence lengths of k.
94
+ The first element should be 0 and the last element should be k.shape[0].
95
+
96
+ Returns:
97
+ output: shape (batch_size, seqlen, dim) or (tot_seqlens, dim) if packing,
98
+ where dim = num_heads * head_dim
99
+ """
100
+ attn_out = flash_attn_varlen_func(
101
+ q,
102
+ k,
103
+ v,
104
+ q_cu_seqlens,
105
+ k_cu_seqlens,
106
+ max_seqlen_q,
107
+ max_seqlen_k,
108
+ causal=False,
109
+ deterministic=deterministic,
110
+ )
111
+ if isinstance(attn_out, tuple):
112
+ attn_out = attn_out[0]
113
+
114
+ attn_out = attn_out.flatten(start_dim=-2)
115
+
116
+ return attn_out
117
+
118
+
119
+ def eager_attention(
120
+ q: torch.Tensor,
121
+ k: torch.Tensor,
122
+ v: torch.Tensor,
123
+ q_cu_seqlens: Optional[torch.Tensor] = None,
124
+ k_cu_seqlens: Optional[torch.Tensor] = None,
125
+ **kwargs,
126
+ ) -> torch.Tensor:
127
+ seq_length = q.shape[0]
128
+ attention_mask = torch.zeros([1, seq_length, seq_length],
129
+ device=q.device,
130
+ dtype=torch.bool)
131
+ for i in range(1, len(q_cu_seqlens)):
132
+ attention_mask[
133
+ ...,
134
+ q_cu_seqlens[i - 1]:q_cu_seqlens[i],
135
+ q_cu_seqlens[i - 1]:q_cu_seqlens[i],
136
+ ] = True
137
+ q = q.transpose(0, 1)
138
+ k = k.transpose(0, 1)
139
+ v = v.transpose(0, 1)
140
+
141
+ attn_weight = q @ k.transpose(-2, -1) / math.sqrt(q.shape[-1])
142
+ attn_weight += attention_mask
143
+ attn_weight = torch.softmax(attn_weight, dim=-1,
144
+ dtype=torch.float32).to(q.dtype)
145
+
146
+ attn_output = attn_weight @ v
147
+ attn_output = attn_output.transpose(0, 1)
148
+ attn_output = attn_output.reshape(seq_length, -1)
149
+ return attn_output
150
+
151
+
152
+ VL_VISION_ATTENTION_FUNCTIONS = {
153
+ "flash_attention_2": multihead_attention,
154
+ "eager": eager_attention,
155
+ }
156
+
157
+
158
+ def _apply_rope_input_validation(x, freqs_cis):
159
+ assert x.ndim == freqs_cis.ndim + 1, (x.shape, freqs_cis.shape)
160
+ assert x.shape[:-2] == freqs_cis.shape[:-1], (x.shape, freqs_cis.shape)
161
+ assert x.shape[-1] == 2 * freqs_cis.shape[-1], (x.shape, freqs_cis.shape)
162
+ assert freqs_cis.dtype == torch.complex64, freqs_cis.dtype
163
+
164
+
165
+ def get_rope_shape_decorate(func):
166
+ _get_rope_shape_first_call_flag = set()
167
+
168
+ def wrapper(org, interpolation_mode, shape):
169
+ key = (org.requires_grad, torch.is_grad_enabled(), interpolation_mode)
170
+ if key not in _get_rope_shape_first_call_flag:
171
+ _get_rope_shape_first_call_flag.add(key)
172
+ _ = func(org, interpolation_mode, shape=(64, 64))
173
+ return func(org, interpolation_mode, shape)
174
+
175
+ return wrapper
176
+
177
+
178
+ @get_rope_shape_decorate
179
+ @torch.compile(dynamic=True)
180
+ def get_rope_shape(org, interpolation_mode, shape):
181
+ return (F.interpolate(
182
+ org.permute((2, 0, 1)).unsqueeze(0),
183
+ size=shape,
184
+ mode=interpolation_mode,
185
+ ).squeeze(0).permute((1, 2, 0)).flatten(end_dim=1))
186
+
187
+
188
+ def apply_rope(xq: torch.Tensor, xk: torch.Tensor,
189
+ freqs_cis: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
190
+ """
191
+ Args: (The leading dimensions of all inputs should be the same)
192
+ xq: query, tensor of shape (..., num_heads, head_dim)
193
+ xk: key, tensor of shape (..., num_heads, head_dim)
194
+ freqs_cis: tensor of shape (..., head_dim/2), dtype=torch.complex64. It contains the precomputed cis(freqs) for each position in the 2D grid.
195
+ Returns:
196
+ xq_out, xk_out: tensors of shape (..., num_heads, head_dim)
197
+ """
198
+ _apply_rope_input_validation(xq, freqs_cis)
199
+ _apply_rope_input_validation(xk, freqs_cis)
200
+
201
+ freqs_cis = freqs_cis.unsqueeze(-2) # ..., 1, head_dim/2
202
+ # ..., num_heads, head_dim/2
203
+ xq_ = torch.view_as_complex(xq.float().view(*xq.shape[:-1], -1, 2))
204
+ xk_ = torch.view_as_complex(xk.float().view(*xq.shape[:-1], -1, 2))
205
+ xq_out = torch.view_as_real(xq_ * freqs_cis).flatten(
206
+ -2) # ..., num_heads, head_dim
207
+ xk_out = torch.view_as_real(xk_ * freqs_cis).flatten(
208
+ -2) # ..., num_heads, head_dim
209
+ return xq_out.type_as(xq), xk_out.type_as(xk)
210
+
211
+
212
+ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
213
+ """
214
+ From:
215
+ https://github.com/OpenGVLab/InternVideo/blob/421f6d2361fc8f61a3394244571f2601a4e99e29/InternVideo2/multi_modality/models/backbones/internvideo2/pos_embed.py#L86
216
+ embed_dim: output dimension for each position
217
+ pos: a list of positions to be encoded: size (M,)
218
+ out: (M, D)
219
+ """
220
+ assert embed_dim % 2 == 0
221
+ omega = np.arange(embed_dim // 2, dtype=np.float32)
222
+ omega /= embed_dim / 2.0
223
+ omega = 1.0 / 10000**omega # (D/2,)
224
+
225
+ pos = pos.reshape(-1) # (M,)
226
+ out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
227
+
228
+ emb_sin = np.sin(out) # (M, D/2)
229
+ emb_cos = np.cos(out) # (M, D/2)
230
+
231
+ emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D)
232
+ return emb
233
+
234
+
235
+ def get_1d_sincos_pos_embed(embed_dim, t_size, cls_token=False):
236
+ """
237
+ t_size: int of the temporal size
238
+ return:
239
+ pos_embed: [t_size, embed_dim] or [1+t_size, embed_dim] (w/ or w/o cls_token)
240
+ """
241
+ grid_t = np.arange(t_size, dtype=np.float32)
242
+ pos_embed = get_1d_sincos_pos_embed_from_grid(embed_dim, grid_t)
243
+ if cls_token:
244
+ pos_embed = np.concatenate([np.zeros([1, embed_dim]), pos_embed],
245
+ axis=0)
246
+ return pos_embed
247
+
248
+
249
+ def _first_layer_key_first_token_vector(past_key_values):
250
+ """``past_key_values[0][0][..., 0]`` for LLaVA-style cache masking (shape ``[batch, heads, seq]``).
251
+ Legacy caches are ``list`` of ``(key, value)`` per layer. Transformers v4.36+ / v5 use ``Cache`` (e.g.
252
+ ``DynamicCache``) with per-layer ``.keys`` tensors instead of subscripting ``[0][0]``.
253
+ """
254
+ if isinstance(past_key_values, Cache):
255
+ layers = getattr(past_key_values, "layers", None) or []
256
+ if not layers:
257
+ return None
258
+ layer0 = layers[0]
259
+ keys = getattr(layer0, "keys", None)
260
+ if keys is None or keys.numel() == 0 or keys.ndim < 4:
261
+ return None
262
+ return keys[:, :, :, 0]
263
+ return past_key_values[0][0][:, :, :, 0]
264
+
265
+
266
+ def _first_layer_past_seq_length(past_key_values):
267
+ """Layer-0 KV cache sequence length (BHSD keys: ``shape[2] == seq_len``).
268
+ """
269
+ if isinstance(past_key_values, Cache):
270
+ try:
271
+ return int(past_key_values.get_seq_length(0))
272
+ except Exception:
273
+ return None
274
+ try:
275
+ k0 = past_key_values[0][0]
276
+ if k0 is None or k0.ndim < 3:
277
+ return None
278
+ return int(k0.shape[2])
279
+ except Exception:
280
+ return None
281
+
282
+
283
+ class Learnable2DInterpPosEmbDivided_fixed(nn.Module):
284
+
285
+ def __init__(self,
286
+ height: int,
287
+ width: int,
288
+ num_frames: int,
289
+ dim: int,
290
+ interpolation_mode: str = 'bicubic') -> None:
291
+ super().__init__()
292
+ self.height = height
293
+ self.width = width
294
+ self.num_frames = num_frames
295
+ self.dim = dim
296
+ self.interpolation_mode = interpolation_mode
297
+ self.weight = nn.Parameter(torch.empty(height, width, dim))
298
+ self.register_buffer('time_weight',
299
+ torch.from_numpy(
300
+ get_1d_sincos_pos_embed(
301
+ self.dim,
302
+ self.num_frames)).float().unsqueeze(1),
303
+ persistent=False)
304
+
305
+ self.reset_parameters()
306
+
307
+ def reset_parameters(self):
308
+ nn.init.normal_(self.weight)
309
+
310
+ def forward(self, x: torch.Tensor,
311
+ grid_thws: torch.Tensor) -> torch.Tensor:
312
+ pos_embs = []
313
+ for t, h, w in grid_thws.tolist():
314
+ assert t <= self.num_frames, f't:{t} > self.num_frames:{self.num_frames}'
315
+ if (h, w) == self.weight.shape[:-1]:
316
+ pos_emb_2d = self.weight.flatten(end_dim=1)
317
+ else:
318
+ pos_emb_2d = get_rope_shape(
319
+ self.weight,
320
+ interpolation_mode=self.interpolation_mode,
321
+ shape=(h, w),
322
+ )
323
+
324
+ if t == 1:
325
+ pos_emb_3d = pos_emb_2d
326
+ else:
327
+ pos_emb_3d = pos_emb_2d.unsqueeze(0).repeat(
328
+ t, 1, 1) + self.time_weight[0:t]
329
+
330
+ pos_embs.append(pos_emb_3d.reshape(-1, pos_emb_3d.shape[-1]))
331
+
332
+ out = x + torch.cat(pos_embs)
333
+ return out
334
+
335
+
336
+ class MoonVision3dPatchEmbed(nn.Module):
337
+
338
+ def __init__(self,
339
+ out_dim: int,
340
+ in_dim: int = 3,
341
+ patch_size: int | tuple[int, int] = (14, 14),
342
+ pos_emb_height: int = 14,
343
+ pos_emb_width: int = 14,
344
+ pos_emb_time: int = 4,
345
+ pos_emb_type: str = 'divided_fixed'):
346
+ super().__init__()
347
+ assert isinstance(
348
+ patch_size,
349
+ int | Sequence), f'Invalid patch_size type: {type(patch_size)}'
350
+ if isinstance(patch_size, int):
351
+ patch_size = (patch_size, patch_size)
352
+ assert (len(patch_size) == 2
353
+ ), f'Expected patch_size to be a tuple of 2, got {patch_size}'
354
+ self.patch_size = patch_size
355
+
356
+ self.proj = nn.Conv2d(in_dim,
357
+ out_dim,
358
+ kernel_size=patch_size,
359
+ stride=patch_size)
360
+
361
+ if pos_emb_type == 'divided_fixed':
362
+ self.pos_emb = Learnable2DInterpPosEmbDivided_fixed(
363
+ height=pos_emb_height,
364
+ width=pos_emb_width,
365
+ num_frames=pos_emb_time,
366
+ dim=out_dim)
367
+ else:
368
+ raise NotImplementedError(
369
+ f'Not support pos_emb_type: {pos_emb_type}')
370
+
371
+ def forward(self, x: torch.Tensor,
372
+ grid_thws: torch.Tensor) -> torch.Tensor:
373
+ """
374
+ Args:
375
+ x (L, Channels): input tensor
376
+ grid_hws (N, 3): temporal, height and width
377
+
378
+ Returns:
379
+ (L, Cout) tensor
380
+ """
381
+ x = self.proj(x).view(x.size(0), -1)
382
+ # apply positional embedding
383
+ x = self.pos_emb(x, grid_thws)
384
+ return x
385
+
386
+
387
+ class Rope2DPosEmbRepeated(nn.Module):
388
+ """2D rotary position embedding with multi-resolution support.
389
+
390
+ This class is intended to be used in the following way:
391
+ 1. Before training, create an instance of Rope2DPosEmb. This instance will hold the precomputed cis.
392
+ 2. Before each forward pass, call `get_freqs_cis_by_*` to get the `freqs_cis` tensor for this iteration.
393
+ 3. During the forward pass, pass the `freqs_cis` tensor to each attention layer, and call `apply` just before each attention operation.
394
+ The rope is shared across all attention layers and all heads.
395
+
396
+ Refs:
397
+ - RoFormer: https://arxiv.org/abs/2104.09864
398
+ - VisionLLaMA: https://arxiv.org/abs/2403.00522
399
+ - https://github.com/Meituan-AutoML/VisionLLaMA/blob/main/dit/models.py
400
+
401
+ Args:
402
+ dim (int): usually the multi-head attention dimension, should be divisible by 4 (TODO: relax this constraint if needed)
403
+ max_height (int): the maximum height of the 2D grid
404
+ max_width (int): the maximum width of the 2D grid
405
+ theta_base (float): the base of the theta
406
+ device (str): the device to store the precomputed cis
407
+ """
408
+
409
+ def __init__(self,
410
+ dim: int,
411
+ max_height: int,
412
+ max_width: int,
413
+ theta_base=10000):
414
+ super().__init__()
415
+ self.dim = dim
416
+ assert self.dim % 4 == 0, 'dim must be divisible by 4'
417
+ self.max_height = max_height
418
+ self.max_width = max_width
419
+ self.theta_base = theta_base
420
+
421
+ def extra_repr(self):
422
+ return f'dim={self.dim}, max_height={self.max_height}, max_width={self.max_width}, theta_base={self.theta_base}'
423
+
424
+ def _precompute_freqs_cis(self, device: torch.device) -> torch.Tensor:
425
+ """Calculate the cis(freqs) for each position in the 2D grid.
426
+
427
+ Return: complex tensor of shape (max_height, max_width, dim//2) and value:
428
+ height axis: ret[h, w, 2*i] = cis(h * theta_base**(-4*i/dim))
429
+ weight axis: ret[h, w, 2*i+1] = cis(w * theta_base**(-4*i/dim)) with (i in [0, dim//4))
430
+ note: `cis` is a mathematical notation defined by cis x = cos x + i sin x,
431
+ """
432
+ N = self.max_height * self.max_width
433
+ flat_pos = torch.arange(0, N).float().to(device)
434
+ x_pos = flat_pos % self.max_width
435
+ y_pos = flat_pos // self.max_width
436
+ dim_range = (torch.arange(0, self.dim,
437
+ 4)[:(self.dim // 4)].float().to(device)
438
+ ) # C/4
439
+ freqs = 1.0 / (self.theta_base**(dim_range / self.dim))
440
+ x_freqs = torch.outer(x_pos, freqs).float() # N, C/4
441
+ y_freqs = torch.outer(y_pos, freqs).float() # N, C/4
442
+ x_cis = torch.polar(torch.ones_like(x_freqs), x_freqs) # N, C/4
443
+ y_cis = torch.polar(torch.ones_like(y_freqs), y_freqs) # N, C/4
444
+ # N, C/4, 2
445
+ freqs_cis = torch.cat(
446
+ [x_cis.unsqueeze(dim=-1),
447
+ y_cis.unsqueeze(dim=-1)], dim=-1)
448
+ # max_height, max_width, C/2
449
+ freqs_cis = freqs_cis.reshape(self.max_height, self.max_width, -1)
450
+ return freqs_cis
451
+
452
+ def get_freqs_cis(self, grid_thws: torch.Tensor,
453
+ device: torch.device) -> torch.Tensor:
454
+ """
455
+ Args:
456
+ grid_thws (torch.Tensor): grid time, height and width
457
+
458
+ Returns:
459
+ freqs_cis: tensor of shape (sum(t * height * width), dim//2)
460
+ """
461
+ if not hasattr(self, 'freqs_cis'):
462
+ self.register_buffer('freqs_cis',
463
+ self._precompute_freqs_cis(device),
464
+ persistent=False)
465
+
466
+ shapes = grid_thws.tolist()
467
+ assert all(1 <= h <= self.max_height and 1 <= w <= self.max_width
468
+ for t, h, w in shapes), (
469
+ shapes,
470
+ self.max_height,
471
+ self.max_width,
472
+ )
473
+ freqs_cis = torch.cat(
474
+ [
475
+ self.freqs_cis[:h, :w].reshape(-1, self.dim // 2).repeat(t, 1)
476
+ for t, h, w in shapes
477
+ ],
478
+ dim=0,
479
+ )
480
+ return freqs_cis
481
+
482
+
483
+ class MLP2(nn.Module):
484
+ """
485
+ Args:
486
+ dims: [in_dim, hidden_dim, out_dim]
487
+ bias: whether to use bias in linear layer.
488
+ """
489
+
490
+ def __init__(self, dims: list[int], activation, bias=True):
491
+ super().__init__()
492
+ assert len(dims) == 3
493
+ self.fc0 = nn.Linear(dims[0], dims[1], bias=bias)
494
+ self.fc1 = nn.Linear(dims[1], dims[2], bias=bias)
495
+ self.activation = activation
496
+ for m in [self.fc0, self.fc1]:
497
+ nn.init.trunc_normal_(m.weight, std=math.sqrt(2 / m.in_features))
498
+ if m.bias is not None:
499
+ nn.init.zeros_(m.bias)
500
+
501
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
502
+ x = self.fc0(x)
503
+ x = self.activation(x)
504
+ return self.fc1(x)
505
+
506
+
507
+ class MoonViTEncoderLayer(nn.Module):
508
+
509
+ def __init__(
510
+ self,
511
+ num_heads: int,
512
+ hidden_dim: int,
513
+ mlp_dim: int,
514
+ *,
515
+ attn_implementation: str = 'flash_attention_2',
516
+ activation=F.gelu,
517
+ attn_bias: bool = False,
518
+ use_deterministic_attn: bool = False,
519
+ ):
520
+ super().__init__()
521
+ self.num_heads = num_heads
522
+ self.hidden_dim = hidden_dim
523
+ self.hidden_size_per_attention_head = self.hidden_dim // self.num_heads
524
+ self.attn_implementation = attn_implementation
525
+ self.use_deterministic_attn = use_deterministic_attn
526
+
527
+ self.norm0 = nn.LayerNorm(hidden_dim)
528
+ self.norm1 = nn.LayerNorm(hidden_dim)
529
+ self.mlp = MLP2([hidden_dim, mlp_dim, hidden_dim], activation)
530
+ self.wqkv = nn.Linear(hidden_dim, hidden_dim * 3, bias=attn_bias)
531
+ self.wo = nn.Linear(hidden_dim, hidden_dim, bias=attn_bias)
532
+
533
+ def attention_qkvpacked(
534
+ self,
535
+ x: torch.Tensor,
536
+ cu_seqlens: torch.Tensor,
537
+ max_seqlen: torch.Tensor,
538
+ rope_freqs_cis: torch.Tensor | None = None,
539
+ ):
540
+ """
541
+ Args:
542
+ x (torch.Tensor): (batch_size, seqlen, hidden_dim)
543
+ cu_seqlens (torch.Tensor):
544
+ """
545
+ xqkv = self.wqkv(x)
546
+
547
+ qkv_shape = xqkv.size()[:-1] + (
548
+ 3,
549
+ self.num_heads,
550
+ self.hidden_size_per_attention_head,
551
+ )
552
+ # xqkv: (batch_size, seqlen, 3, nheads, headdim)
553
+ xqkv = xqkv.view(*qkv_shape)
554
+ xq, xk, xv = torch.unbind(xqkv, dim=-3)
555
+
556
+ xq, xk = apply_rope(xq, xk, rope_freqs_cis)
557
+
558
+ attn_func = VL_VISION_ATTENTION_FUNCTIONS[self.attn_implementation]
559
+ attn_out = attn_func(xq,
560
+ xk,
561
+ xv,
562
+ q_cu_seqlens=cu_seqlens,
563
+ k_cu_seqlens=cu_seqlens,
564
+ max_seqlen_k=max_seqlen,
565
+ max_seqlen_q=max_seqlen,
566
+ deterministic=self.use_deterministic_attn)
567
+
568
+ attn_out = self.wo(attn_out)
569
+ return attn_out
570
+
571
+ def forward(
572
+ self,
573
+ hidden_states: torch.Tensor,
574
+ cu_seqlens: torch.Tensor,
575
+ max_seqlen: int,
576
+ rope_freqs_cis: torch.Tensor | None = None,
577
+ ):
578
+ residual = hidden_states
579
+ hidden_states = self.norm0(hidden_states)
580
+
581
+ hidden_states = self.attention_qkvpacked(hidden_states, cu_seqlens,
582
+ max_seqlen, rope_freqs_cis)
583
+ hidden_states = residual + hidden_states
584
+
585
+ residual = hidden_states
586
+ hidden_states = self.norm1(hidden_states)
587
+ hidden_states = self.mlp(hidden_states)
588
+ hidden_states = residual + hidden_states
589
+
590
+ return hidden_states
591
+
592
+
593
+ class MoonViT3dEncoder(nn.Module):
594
+
595
+ def __init__(self,
596
+ hidden_dim: int,
597
+ num_layers: int,
598
+ block_cfg: dict,
599
+ video_attn_type: str = 'spatial_temporal',
600
+ use_deterministic_attn: bool = False) -> None:
601
+ super().__init__()
602
+
603
+ assert video_attn_type == 'spatial_temporal', f'video_attn_type must be "spatial_temporal", got {video_attn_type}'
604
+ self.video_attn_type = video_attn_type
605
+ self.rope_2d = Rope2DPosEmbRepeated(
606
+ block_cfg['hidden_dim'] // block_cfg['num_heads'], 512, 512)
607
+ self.blocks = nn.ModuleList([
608
+ MoonViTEncoderLayer(**block_cfg,
609
+ use_deterministic_attn=use_deterministic_attn)
610
+ for _ in range(num_layers)
611
+ ])
612
+ self.final_layernorm = nn.LayerNorm(hidden_dim)
613
+
614
+ def forward(
615
+ self,
616
+ hidden_states: torch.Tensor,
617
+ grid_thws: torch.Tensor,
618
+ ) -> torch.Tensor:
619
+ rope_freqs_cis = self.rope_2d.get_freqs_cis(
620
+ grid_thws=grid_thws, device=hidden_states.device)
621
+
622
+ lengths = torch.cat((
623
+ torch.zeros(1, dtype=grid_thws.dtype, device=grid_thws.device),
624
+ grid_thws[:, 0] * grid_thws[:, 1] * grid_thws[:, 2],
625
+ ))
626
+
627
+ max_seqlen = lengths.max()
628
+ cu_seqlens = lengths.to(hidden_states.device).cumsum(dim=0,
629
+ dtype=torch.int32)
630
+ for block in self.blocks:
631
+ hidden_states = block(hidden_states,
632
+ cu_seqlens,
633
+ max_seqlen,
634
+ rope_freqs_cis=rope_freqs_cis)
635
+
636
+ hidden_states = self.final_layernorm(hidden_states)
637
+ return hidden_states
638
+
639
+
640
+ def tpool_patch_merger(
641
+ x: torch.Tensor,
642
+ grid_thws: torch.Tensor,
643
+ merge_kernel_size: tuple[int, int] = (2, 2),
644
+ ) -> list[torch.Tensor]:
645
+ d_model = x.size(-1)
646
+
647
+ outputs = []
648
+ pre_sum = 0
649
+ for t, h, w in grid_thws.tolist():
650
+ # Get the current sequence
651
+ seq = x[pre_sum:pre_sum + t * h * w]
652
+ # Reshape along self.merge_kernel_size and concat to the last dimension
653
+ kernel_height, kernel_width = merge_kernel_size
654
+ new_height, new_width = h // kernel_height, w // kernel_width
655
+ reshaped_seq = seq.view(t, new_height, kernel_height, new_width,
656
+ kernel_width, d_model)
657
+ reshaped_seq = reshaped_seq.permute(0, 1,
658
+ 3, 2, 4, 5).contiguous().mean(
659
+ dim=0) # temporal pooling
660
+ padded_seq = reshaped_seq.view(new_height * new_width,
661
+ kernel_height * kernel_width, -1)
662
+ outputs.append(padded_seq)
663
+ pre_sum += t * h * w
664
+
665
+ return outputs
666
+
667
+
668
+ class MoonViT3dPretrainedModel(PreTrainedModel):
669
+ config_class = None
670
+ model_type = 'moonvit3d'
671
+ _no_split_modules = ['PackingTransformer']
672
+ _supports_flash_attn_2 = True
673
+ _supports_flash_attn = True
674
+ _supports_sdpa = True
675
+
676
+ def __init__(self, config, *inputs, **kwargs):
677
+ super().__init__(config, *inputs, **kwargs)
678
+ config = deepcopy(config)
679
+ self.merge_kernel_size = config.merge_kernel_size
680
+ self.patch_size = config.patch_size
681
+ self.merge_type = config.merge_type
682
+
683
+ self.patch_embed = MoonVision3dPatchEmbed(
684
+ out_dim=config.hidden_size,
685
+ patch_size=config.patch_size,
686
+ pos_emb_height=config.init_pos_emb_height,
687
+ pos_emb_width=config.init_pos_emb_width,
688
+ pos_emb_time=config.init_pos_emb_time,
689
+ pos_emb_type=config.pos_emb_type,
690
+ )
691
+
692
+ self.encoder = MoonViT3dEncoder(hidden_dim=config.hidden_size,
693
+ num_layers=config.num_hidden_layers,
694
+ block_cfg={
695
+ 'num_heads':
696
+ config.num_attention_heads,
697
+ 'hidden_dim':
698
+ config.hidden_size,
699
+ 'mlp_dim':
700
+ config.intermediate_size,
701
+ 'activation':
702
+ PytorchGELUTanh(),
703
+ 'attn_bias':
704
+ True,
705
+ 'attn_implementation':
706
+ config._attn_implementation,
707
+ },
708
+ video_attn_type=config.video_attn_type)
709
+
710
+ def forward(self, pixel_values: torch.Tensor,
711
+ grid_thws: torch.Tensor) -> torch.Tensor:
712
+ """
713
+ Args:
714
+ pixel_values (torch.Tensor): The input pixel values.
715
+ grid_thws (torch.Tensor): Temporal, height and width.
716
+
717
+ Returns:
718
+ torch.Tensor: The output tokens.
719
+ """
720
+ # grid_thws = grid_thws.to('cpu')
721
+ assert grid_thws.ndim == 2, f'grid_thws should be 2D, got {grid_thws.ndim}'
722
+ assert grid_thws.size(1) == 3, f'No support for thw: {grid_thws}'
723
+ hidden_states = self.patch_embed(pixel_values, grid_thws)
724
+ hidden_states = self.encoder(hidden_states, grid_thws)
725
+ if self.merge_type == 'sd2_tpool': # spatial downsampling 2x with temporal pooling all
726
+ hidden_states = tpool_patch_merger(
727
+ hidden_states,
728
+ grid_thws,
729
+ merge_kernel_size=self.merge_kernel_size)
730
+ else:
731
+ raise NotImplementedError(f'Not support {self.merge_type}')
732
+
733
+ return hidden_states
734
+
735
+
736
+ # ============================================================================
737
+ # MM Projector Helper Classes (from mm_projector/modeling_mm_projectors.py)
738
+ # ============================================================================
739
+
740
+
741
+ class IdentityMap(nn.Module):
742
+
743
+ def __init__(self):
744
+ super().__init__()
745
+
746
+ def forward(self, x, *args, **kwargs):
747
+ return x
748
+
749
+
750
+ class MLP(nn.Module):
751
+
752
+ def __init__(self, config):
753
+ super().__init__()
754
+ # TODO, use faster LayerNorm
755
+ self.pre_norm = nn.LayerNorm(config.mm_hidden_size)
756
+ self.proj = nn.Sequential(
757
+ nn.Linear(config.mm_hidden_size, config.hidden_size), nn.GELU(),
758
+ nn.Linear(config.hidden_size, config.hidden_size))
759
+
760
+ def forward(self, x, *args, **kwargs):
761
+ assert isinstance(x,
762
+ list | tuple), f'x is not a list or tuple: {type(x)}'
763
+ lengths = [item.shape[0] for item in x]
764
+ x = torch.cat(x, dim=0)
765
+ x = self.pre_norm(x)
766
+ x = self.proj(x)
767
+ x = torch.split(x, lengths, dim=0)
768
+
769
+ return x
770
+
771
+
772
+ class PatchMergerMLP(nn.Module):
773
+
774
+ def __init__(self, config):
775
+ super().__init__()
776
+ eps = config.projector_ln_eps
777
+ self.hidden_size = config.mm_hidden_size * (
778
+ config.merge_kernel_size[0] * config.merge_kernel_size[1])
779
+ self.pre_norm = nn.LayerNorm(config.mm_hidden_size, eps=eps)
780
+ self.proj = nn.Sequential(
781
+ nn.Linear(self.hidden_size, self.hidden_size),
782
+ nn.GELU(),
783
+ nn.Linear(self.hidden_size, config.hidden_size),
784
+ )
785
+
786
+ def forward(self, x, *args, **kwargs):
787
+ if isinstance(x, list) or isinstance(x, tuple):
788
+ x = [
789
+ self.proj(self.pre_norm(item).view(item.shape[0], -1))
790
+ for item in x
791
+ ]
792
+ else:
793
+ # B, N, N_k, C = x.shape
794
+ B = x.shape[0]
795
+ x = self.proj(self.pre_norm(x).view(B, -1, self.hidden_size))
796
+ return x
797
+
798
+
799
+ class KimiK25PreTrainedModel(PreTrainedModel):
800
+ config_class = KimiK25Config
801
+ base_model_prefix = "model"
802
+ _no_split_modules = [
803
+ "MoonViT3dPretrainedModel",
804
+ "MoonViTEncoderLayer",
805
+ "DeepseekDecoderLayer",
806
+ "PatchMergerMLP",
807
+ ]
808
+ _skip_keys_device_placement = "past_key_values"
809
+ _supports_flash_attn_2 = True
810
+ _supports_flash_attn = True
811
+ _supports_sdpa = False
812
+
813
+ def _init_weights(self, module):
814
+ # important: this ported version of Llava isn't meant for training from scratch - only
815
+ # inference and fine-tuning - so the proper init weights code has been removed - the original codebase
816
+ # https://github.com/haotian-liu/LLaVA/tree/main/llava should serve for that purpose
817
+ std = (self.config.initializer_range if hasattr(
818
+ self.config, "initializer_range") else
819
+ self.config.text_config.initializer_range)
820
+
821
+ if hasattr(module, "class_embedding"):
822
+ module.class_embedding.data.normal_(mean=0.0, std=std)
823
+
824
+ if isinstance(module, (nn.Linear, nn.Conv2d)):
825
+ module.weight.data.normal_(mean=0.0, std=std)
826
+ if module.bias is not None:
827
+ module.bias.data.zero_()
828
+ elif isinstance(module, nn.Embedding):
829
+ module.weight.data.normal_(mean=0.0, std=std)
830
+ if module.padding_idx is not None:
831
+ module.weight.data[module.padding_idx].zero_()
832
+
833
+
834
+ class VisionTowerConfig(PretrainedConfig):
835
+ model_type = 'moonvit3d'
836
+
837
+ def __init__(self, config: KimiK25Config, **kwargs):
838
+ super().__init__(**kwargs)
839
+ self.patch_size = config.patch_size
840
+ self.init_pos_emb_height = config.init_pos_emb_height
841
+ self.init_pos_emb_width = config.init_pos_emb_width
842
+ self.init_pos_emb_time = config.init_pos_emb_time
843
+ self.pos_emb_type = config.pos_emb_type
844
+ self.num_attention_heads = config.vt_num_attention_heads
845
+ self.num_hidden_layers = config.vt_num_hidden_layers
846
+ self.hidden_size = config.vt_hidden_size
847
+ self.intermediate_size = config.vt_intermediate_size
848
+ self.merge_kernel_size = config.merge_kernel_size
849
+ self.video_attn_type = config.video_attn_type
850
+ self.merge_type = config.merge_type
851
+ self._attn_implementation = config._attn_implementation
852
+
853
+
854
+ class ProjectorConfig:
855
+
856
+ def __init__(self, config: KimiK25Config):
857
+ self.mm_projector_type = config.mm_projector_type
858
+ self.mm_hidden_size = config.mm_hidden_size
859
+ self.hidden_size = config.text_hidden_size
860
+ self.merge_kernel_size = config.merge_kernel_size
861
+ self.projector_hidden_act = config.projector_hidden_act
862
+ self.projector_ln_eps = config.projector_ln_eps
863
+
864
+
865
+ # ref https://github.com/huggingface/transformers/blob/78b2929c0554b79e0489b451ce4ece14d265ead2/src/transformers/models/llava/modeling_llava.py#L240
866
+ class KimiK25ForConditionalGeneration(KimiK25PreTrainedModel):
867
+
868
+ def __init__(self, config: KimiK25Config):
869
+ super().__init__(config)
870
+
871
+ vt_config = VisionTowerConfig(config.vision_config)
872
+ self.vision_tower = MoonViT3dPretrainedModel(vt_config)
873
+
874
+ proj_config = ProjectorConfig(config.vision_config)
875
+ if proj_config.mm_projector_type == 'identity':
876
+ self.mm_projector = IdentityMap()
877
+ elif proj_config.mm_projector_type == 'mlp':
878
+ self.mm_projector = MLP(proj_config)
879
+ elif proj_config.mm_projector_type == 'patchmerger':
880
+ self.mm_projector = PatchMergerMLP(proj_config)
881
+ else:
882
+ raise ValueError(
883
+ f"Unsupported mm_projector_type: {proj_config.mm_projector_type}"
884
+ )
885
+
886
+ self.language_model = DeepseekV3ForCausalLM(config.text_config)
887
+ self.post_init()
888
+
889
+ if hasattr(self.language_model, 'dtype'):
890
+ target_dtype = self.language_model.dtype
891
+ self.vision_tower = self.vision_tower.to(dtype=target_dtype)
892
+ self.mm_projector = self.mm_projector.to(dtype=target_dtype)
893
+
894
+ def get_input_embeddings(self):
895
+ return self.language_model.get_input_embeddings()
896
+
897
+ def set_input_embeddings(self, value):
898
+ self.language_model.set_input_embeddings(value)
899
+
900
+ def get_output_embeddings(self):
901
+ return self.language_model.get_output_embeddings()
902
+
903
+ def set_output_embeddings(self, new_embeddings):
904
+ self.language_model.set_output_embeddings(new_embeddings)
905
+
906
+ def set_decoder(self, decoder):
907
+ self.language_model.set_decoder(decoder)
908
+
909
+ def get_decoder(self):
910
+ return self.language_model.get_decoder()
911
+
912
+ def tie_weights(self, *args, **kwargs):
913
+ # Transformers >=5 passes ``missing_keys`` / ``recompute_mapping``; forward for the text backbone only.
914
+ return self.language_model.tie_weights(*args, **kwargs)
915
+
916
+ def resize_token_embeddings(self,
917
+ new_num_tokens: int | None = None,
918
+ pad_to_multiple_of=None) -> nn.Embedding:
919
+ model_embeds = self.language_model.resize_token_embeddings(
920
+ new_num_tokens, pad_to_multiple_of)
921
+ # update vocab size
922
+ self.config.text_config.vocab_size = model_embeds.num_embeddings
923
+ self.vocab_size = model_embeds.num_embeddings
924
+ return model_embeds
925
+
926
+ def _merge_input_ids_with_image_features(
927
+ self,
928
+ image_features: list[torch.Tensor],
929
+ inputs_embeds: torch.Tensor,
930
+ input_ids: torch.Tensor,
931
+ attention_mask: torch.Tensor,
932
+ labels: torch.Tensor | None = None,
933
+ ):
934
+ """
935
+ Args:
936
+ image_features (:obj:`torch.Tensor` of shape :obj:`(num_image_tokens, embed_dim)`):
937
+ The image features to merge with the input embeddings.
938
+ inputs_embeds (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length, embed_dim)`):
939
+ The input embeddings.
940
+ input_ids (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length)`):
941
+ The input ids.
942
+ attention_mask (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length)`):
943
+ The attention mask.
944
+ labels (:obj:`torch.Tensor` of shape :obj:`(batch_size, sequence_length)`, *optional*):
945
+ The labels.
946
+ """
947
+ _, embed_dim = image_features[0].shape
948
+ feature_lengths = [x.shape[0] for x in image_features]
949
+ image_features = torch.cat(image_features, dim=0)
950
+
951
+ image_token_index: int = self.config.media_placeholder_token_id
952
+ pad_token_id: int = self.config.pad_token_id
953
+ ignore_index: int = self.config.ignore_index
954
+
955
+ batch_size, sequence_length = input_ids.shape
956
+ left_padding = not torch.sum(
957
+ input_ids[:, -1] == torch.tensor(pad_token_id))
958
+
959
+ # 1. Create a mask to know where special image tokens are
960
+ _token_occupation_table = torch.ones_like(input_ids.flatten())
961
+ _token_occupation_table[input_ids.flatten() ==
962
+ image_token_index] = torch.tensor(
963
+ feature_lengths,
964
+ dtype=torch.long,
965
+ device=input_ids.device)
966
+ _token_occupation_table = _token_occupation_table.reshape(
967
+ input_ids.shape)
968
+
969
+ max_embed_dim = _token_occupation_table.sum(-1).max().item()
970
+ assert (
971
+ max_embed_dim >= sequence_length
972
+ ), f"The maximum embedding dimension ({max_embed_dim}) is less than the sequence length ({sequence_length})"
973
+ batch_indices, non_image_indices = torch.where(
974
+ input_ids != image_token_index)
975
+
976
+ # 2. Compute the positions where text should be written
977
+ # Calculate new positions for text tokens in merged image-text sequence.
978
+ new_token_positions = torch.cumsum(_token_occupation_table, -1) - 1
979
+ nb_image_pad = max_embed_dim - 1 - new_token_positions[:, -1]
980
+ if left_padding:
981
+ new_token_positions += nb_image_pad[:,
982
+ None] # offset for left padding
983
+ text_to_overwrite = new_token_positions[batch_indices,
984
+ non_image_indices]
985
+
986
+ # 3. Create the full embedding, already padded to the maximum position
987
+ final_embedding = torch.zeros(
988
+ batch_size,
989
+ max_embed_dim,
990
+ embed_dim,
991
+ dtype=inputs_embeds.dtype,
992
+ device=inputs_embeds.device,
993
+ )
994
+ final_attention_mask = torch.zeros(batch_size,
995
+ max_embed_dim,
996
+ dtype=attention_mask.dtype,
997
+ device=inputs_embeds.device)
998
+ if labels is not None:
999
+ final_labels = torch.full(
1000
+ (batch_size, max_embed_dim),
1001
+ ignore_index,
1002
+ dtype=input_ids.dtype,
1003
+ device=input_ids.device,
1004
+ )
1005
+ # In case the Vision model or the Language model has been offloaded to CPU, we need to manually
1006
+ # set the corresponding tensors into their correct target device.
1007
+ target_device = inputs_embeds.device
1008
+ batch_indices, non_image_indices, text_to_overwrite = (
1009
+ batch_indices.to(target_device),
1010
+ non_image_indices.to(target_device),
1011
+ text_to_overwrite.to(target_device),
1012
+ )
1013
+ attention_mask = attention_mask.to(target_device)
1014
+
1015
+ # 4. Fill the embeddings based on the mask.
1016
+ final_embedding[batch_indices,
1017
+ text_to_overwrite] = inputs_embeds[batch_indices,
1018
+ non_image_indices]
1019
+ final_attention_mask[batch_indices,
1020
+ text_to_overwrite] = attention_mask[
1021
+ batch_indices, non_image_indices]
1022
+ if labels is not None:
1023
+ final_labels[batch_indices,
1024
+ text_to_overwrite] = labels[batch_indices,
1025
+ non_image_indices]
1026
+
1027
+ # 5. Fill the embeddings corresponding to the images. Anything that is not `text_positions` needs filling (#29835)
1028
+ image_to_overwrite = torch.full((batch_size, max_embed_dim),
1029
+ True,
1030
+ dtype=torch.bool,
1031
+ device=inputs_embeds.device)
1032
+ image_to_overwrite[batch_indices, text_to_overwrite] = False
1033
+ image_to_overwrite &= image_to_overwrite.cumsum(
1034
+ -1) - 1 >= nb_image_pad[:, None].to(target_device)
1035
+
1036
+ if image_to_overwrite.sum() != image_features.shape[:-1].numel():
1037
+ raise ValueError(
1038
+ f"The input provided to the model are wrong. The number of image tokens is {image_to_overwrite.sum()} while"
1039
+ f" the number of image features given to the model is {image_features.shape[:-1].numel()}. "
1040
+ "This prevents correct indexing and breaks batch generation.")
1041
+
1042
+ final_embedding[image_to_overwrite] = (
1043
+ image_features.contiguous().reshape(-1,
1044
+ embed_dim).to(target_device))
1045
+ final_attention_mask |= image_to_overwrite
1046
+ position_ids = (final_attention_mask.cumsum(-1) - 1).masked_fill_(
1047
+ (final_attention_mask == 0), 1)
1048
+
1049
+ # 6. Mask out the embedding at padding positions, as we later use the past_key_value value to determine the non-attended tokens.
1050
+ batch_indices, pad_indices = torch.where(input_ids == pad_token_id)
1051
+ indices_to_mask = new_token_positions[batch_indices, pad_indices]
1052
+
1053
+ final_embedding[batch_indices, indices_to_mask] = 0
1054
+
1055
+ if labels is None:
1056
+ final_labels = None
1057
+
1058
+ return final_embedding, final_attention_mask, final_labels, position_ids
1059
+
1060
+ def _extract_image_features(self, pixel_values: torch.Tensor,
1061
+ grid_thws: torch.Tensor) -> list[torch.Tensor]:
1062
+ """
1063
+ Args:
1064
+ pixel_values (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, num_channels, height, width)`):
1065
+ The pixel values of the images processed by image processor.
1066
+ grid_thws (:obj:`torch.Tensor` of shape :obj:`(batch_size, 3)`):
1067
+ The grid, height, width of the images.
1068
+
1069
+ Returns:
1070
+ selected_image_feature (:obj:`torch.FloatTensor` of shape :obj:`(num_image_tokens, embed_dim)`):
1071
+ The selected image features to use as input to the projector head.
1072
+
1073
+ """
1074
+
1075
+ target_dtype = self.vision_tower.patch_embed.proj.weight.dtype
1076
+ pixel_values = pixel_values.to(target_dtype)
1077
+
1078
+ image_features = self.vision_tower(pixel_values, grid_thws)
1079
+ return image_features
1080
+
1081
+ def forward(
1082
+ self,
1083
+ input_ids: torch.LongTensor | None = None,
1084
+ pixel_values: torch.FloatTensor | list[torch.FloatTensor]
1085
+ | None = None,
1086
+ grid_thws: torch.Tensor | None = None,
1087
+ attention_mask: torch.Tensor | None = None,
1088
+ position_ids: torch.LongTensor | None = None,
1089
+ past_key_values: list[torch.FloatTensor] | None = None,
1090
+ inputs_embeds: torch.FloatTensor | None = None,
1091
+ labels: torch.LongTensor | None = None,
1092
+ use_cache: bool | None = None,
1093
+ output_attentions: bool | None = None,
1094
+ output_hidden_states: bool | None = None,
1095
+ return_dict: bool | None = None,
1096
+ ) -> tuple | LlavaCausalLMOutputWithPast:
1097
+ r"""
1098
+ Args:
1099
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1100
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
1101
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
1102
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
1103
+
1104
+ ```"""
1105
+ assert self.vision_tower is not None, "vision_tower is not loaded"
1106
+ output_attentions = (output_attentions if output_attentions is not None
1107
+ else self.config.output_attentions)
1108
+ output_hidden_states = (output_hidden_states
1109
+ if output_hidden_states is not None else
1110
+ self.config.output_hidden_states)
1111
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
1112
+
1113
+ if inputs_embeds is None:
1114
+ # 1. Extra the input embeddings
1115
+ inputs_embeds = self.get_input_embeddings()(input_ids)
1116
+
1117
+ # 2. Merge text and images
1118
+ if pixel_values is not None and len(
1119
+ pixel_values) > 0 and input_ids.shape[1] != 1:
1120
+ image_features = self._extract_image_features(
1121
+ pixel_values, grid_thws)
1122
+ if self.mm_projector:
1123
+ image_features = self.mm_projector(image_features)
1124
+
1125
+ inputs_embeds = inputs_embeds.to(
1126
+ image_features[0].dtype) # num_tokens, embed_dim
1127
+ inputs_embeds, attention_mask, labels, position_ids = (
1128
+ self._merge_input_ids_with_image_features(
1129
+ image_features,
1130
+ inputs_embeds,
1131
+ input_ids,
1132
+ attention_mask,
1133
+ labels,
1134
+ ))
1135
+
1136
+ # In case input_ids.shape[1] == 1 & pixel_values==None & past_key_values != None, we are in the case of
1137
+ # generation with cache
1138
+ elif (past_key_values is not None and pixel_values is not None
1139
+ and input_ids.shape[1] == 1):
1140
+ first_layer_past_key_value = _first_layer_key_first_token_vector(
1141
+ past_key_values)
1142
+ if first_layer_past_key_value is not None:
1143
+ # Sum all dimensions of head_dim (-2) to avoid random errors such as: https://github.com/huggingface/transformers/pull/28032#issuecomment-1863691941
1144
+ batch_index, non_attended_tokens = torch.where(
1145
+ first_layer_past_key_value.float().sum(-2) == 0)
1146
+
1147
+ # Get the target length
1148
+ target_length = input_ids.shape[1]
1149
+ past_length = _first_layer_past_seq_length(past_key_values)
1150
+ if past_length is None:
1151
+ past_length = int(first_layer_past_key_value.shape[-1])
1152
+
1153
+ extended_attention_mask = torch.ones(
1154
+ (attention_mask.shape[0], past_length),
1155
+ dtype=attention_mask.dtype,
1156
+ device=attention_mask.device,
1157
+ )
1158
+
1159
+ # Filter out only the tokens that can be un-attended, this can happen
1160
+ # if one uses Llava + Fused modules where the cache on the
1161
+ # first iteration is already big enough, or if one passes custom cache
1162
+ valid_indices = non_attended_tokens < extended_attention_mask.size(
1163
+ -1)
1164
+ new_batch_index = batch_index[valid_indices]
1165
+ new_non_attended_tokens = non_attended_tokens[
1166
+ valid_indices]
1167
+
1168
+ # Zero-out the places where we don't need to attend
1169
+ extended_attention_mask[new_batch_index,
1170
+ new_non_attended_tokens] = 0
1171
+
1172
+ attention_mask = torch.cat(
1173
+ (extended_attention_mask,
1174
+ attention_mask[:, -target_length:]),
1175
+ dim=1)
1176
+ position_ids = torch.sum(attention_mask,
1177
+ dim=1).unsqueeze(-1) - 1
1178
+
1179
+ outputs = self.language_model(
1180
+ attention_mask=attention_mask,
1181
+ position_ids=position_ids,
1182
+ past_key_values=past_key_values,
1183
+ inputs_embeds=inputs_embeds,
1184
+ use_cache=use_cache,
1185
+ output_attentions=output_attentions,
1186
+ output_hidden_states=output_hidden_states,
1187
+ return_dict=return_dict,
1188
+ )
1189
+
1190
+ logits = outputs[0]
1191
+
1192
+ loss = None
1193
+ if labels is not None:
1194
+ # Shift so that tokens < n predict n
1195
+ if attention_mask is not None:
1196
+ shift_attention_mask = attention_mask[..., 1:]
1197
+ shift_logits = logits[..., :-1, :][shift_attention_mask.to(
1198
+ logits.device) != 0].contiguous()
1199
+ shift_labels = labels[..., 1:][shift_attention_mask.to(
1200
+ labels.device) != 0].contiguous()
1201
+ else:
1202
+ shift_logits = logits[..., :-1, :].contiguous()
1203
+ shift_labels = labels[..., 1:].contiguous()
1204
+ # Flatten the tokens
1205
+ loss_fct = nn.CrossEntropyLoss()
1206
+ loss = loss_fct(
1207
+ shift_logits.view(-1, shift_logits.size(-1)),
1208
+ shift_labels.view(-1).to(shift_logits.device),
1209
+ )
1210
+
1211
+ if not return_dict:
1212
+ output = (logits, ) + outputs[1:]
1213
+ return (loss, ) + output if loss is not None else output
1214
+
1215
+ return LlavaCausalLMOutputWithPast(
1216
+ loss=loss,
1217
+ logits=logits,
1218
+ past_key_values=outputs.past_key_values,
1219
+ hidden_states=outputs.hidden_states,
1220
+ attentions=outputs.attentions,
1221
+ )
1222
+
1223
+ def prepare_inputs_for_generation(
1224
+ self,
1225
+ input_ids,
1226
+ past_key_values=None,
1227
+ inputs_embeds=None,
1228
+ pixel_values=None,
1229
+ grid_thws=None,
1230
+ attention_mask=None,
1231
+ **kwargs,
1232
+ ):
1233
+ if past_key_values is not None:
1234
+ if isinstance(past_key_values, Cache):
1235
+ cache_length = past_key_values.get_seq_length()
1236
+ past_length = getattr(past_key_values, 'seen_tokens',
1237
+ cache_length)
1238
+ else:
1239
+ cache_length = past_length = past_key_values[0][0].shape[2]
1240
+
1241
+ # Keep only the unprocessed tokens:
1242
+ # 1 - If the length of the attention_mask exceeds the length of input_ids, then we are in a setting where
1243
+ # some of the inputs are exclusively passed as part of the cache (e.g. when passing input_embeds as
1244
+ # input)
1245
+ if attention_mask is not None and attention_mask.shape[
1246
+ 1] > input_ids.shape[1]:
1247
+ input_ids = input_ids[:, -(attention_mask.shape[1] -
1248
+ past_length):]
1249
+ # 2 - If the past_length is smaller than input_ids', then input_ids holds all input tokens. We can discard
1250
+ # input_ids based on the past_length.
1251
+ elif past_length < input_ids.shape[1]:
1252
+ input_ids = input_ids[:, past_length:]
1253
+ # 3 - Otherwise (past_length >= input_ids.shape[1]), let's assume input_ids only has unprocessed tokens.
1254
+ elif self.config.media_placeholder_token_id in input_ids:
1255
+ input_ids = input_ids[:, input_ids.shape[1] - 1:]
1256
+ # If the cache has seen more tokens than it can hold, then the cache has a size limit. Let's discard the
1257
+ # older attention values, as their corresponding values are not part of the input.
1258
+ if cache_length < past_length and attention_mask is not None:
1259
+ attention_mask = attention_mask[:, -(cache_length +
1260
+ input_ids.shape[1]):]
1261
+
1262
+ position_ids = kwargs.get("position_ids", None)
1263
+ if attention_mask is not None and position_ids is None:
1264
+ # create position_ids on the fly for batch generation
1265
+ position_ids = attention_mask.long().cumsum(-1) - 1
1266
+ position_ids.masked_fill_(attention_mask == 0, 1)
1267
+ if past_key_values:
1268
+ position_ids = position_ids[:, -input_ids.shape[1]:]
1269
+
1270
+ # Generation (especially transformers v5) may supply ``position_ids`` for the full sequence while
1271
+ # ``input_ids`` here is only the new suffix (e.g. length 1). RoPE must index with the current step length.
1272
+ if past_key_values is not None and position_ids is not None:
1273
+ cur_len = input_ids.shape[1]
1274
+ if position_ids.shape[-1] > cur_len:
1275
+ position_ids = position_ids[..., -cur_len:]
1276
+
1277
+ # if `inputs_embeds` are passed, we only want to use them in the 1st generation step
1278
+ if inputs_embeds is not None and past_key_values is None:
1279
+ model_inputs = {"inputs_embeds": inputs_embeds}
1280
+ else:
1281
+ model_inputs = {"input_ids": input_ids}
1282
+
1283
+ model_inputs.update({
1284
+ "position_ids": position_ids,
1285
+ "past_key_values": past_key_values,
1286
+ "use_cache": kwargs.get("use_cache"),
1287
+ "attention_mask": attention_mask,
1288
+ "pixel_values": pixel_values,
1289
+ "grid_thws": grid_thws,
1290
+ })
1291
+ return model_inputs
1292
+
1293
+ def _reorder_cache(self, *args, **kwargs):
1294
+ return self.language_model._reorder_cache(*args, **kwargs)
tokenization_kimi.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from collections import OrderedDict
3
+ from logging import getLogger
4
+ from pathlib import Path
5
+ from shutil import copyfile
6
+ from typing import Any, Dict, Iterator, List, Optional, Tuple, Union, cast
7
+
8
+ import tiktoken
9
+ from tiktoken.load import load_tiktoken_bpe
10
+ from tokenizers import AddedToken
11
+ from transformers.convert_slow_tokenizer import bytes_to_unicode
12
+ from transformers.tokenization_utils import PreTrainedTokenizer
13
+
14
+ from .tool_declaration_ts import encode_tools_to_typescript_style
15
+
16
+ logger = getLogger(__name__)
17
+ VOCAB_FILES_NAMES = {"vocab_file": "tiktoken.model"}
18
+
19
+
20
+ class TikTokenTokenizer(PreTrainedTokenizer):
21
+ """
22
+ Tokenizing and encoding/decoding text using the Tiktoken tokenizer. See megatron/tokenizer/tiktoken_tokenizer.py.
23
+
24
+ This tokenizer inherits from [`PreTrainedTokenizer`] which contains most of the main methods. Users should refer to
25
+ this superclass for more information regarding those methods.
26
+
27
+ Args:
28
+ vocab_file (`str`):
29
+ The path to the Tiktoken model file.
30
+ bos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|begin_of_text|>",`):
31
+ The beginning of sequence token that was used during pretraining. Can be used a sequence classifier token.
32
+ eos_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|end_of_text|>"`):
33
+ The end of sequence token.
34
+ unk_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|reserved_special_token_249|>"`):
35
+ The unknown token. A token that is not in the vocabulary cannot be converted to an ID and is set to be this
36
+ token instead. The second to last item in special_tokens.
37
+ pad_token (`str` or `tokenizers.AddedToken`, *optional*, defaults to `"<|reserved_special_token_250|>"`):
38
+ The token used for padding, for example when batching sequences of different lengths.
39
+ additional_special_tokens (list of `str`, *optional*):
40
+ A tuple or a list of additional tokens, which will be marked as `special`, meaning that they will be
41
+ skipped when decoding if `skip_special_tokens` is set to `True`.
42
+ """
43
+
44
+ vocab_files_names = VOCAB_FILES_NAMES
45
+
46
+ model_input_names = ["input_ids", "attention_mask"]
47
+
48
+ special_tokens: Dict[str, int]
49
+
50
+ num_reserved_special_tokens = 256
51
+
52
+ pat_str = "|".join([
53
+ r"""[\p{Han}]+""",
54
+ r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]*[\p{Ll}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]+(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
55
+ r"""[^\r\n\p{L}\p{N}]?[\p{Lu}\p{Lt}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]+[\p{Ll}\p{Lm}\p{Lo}\p{M}&&[^\p{Han}]]*(?i:'s|'t|'re|'ve|'m|'ll|'d)?""",
56
+ r"""\p{N}{1,3}""",
57
+ r""" ?[^\s\p{L}\p{N}]+[\r\n]*""",
58
+ r"""\s*[\r\n]+""",
59
+ r"""\s+(?!\S)""",
60
+ r"""\s+""",
61
+ ])
62
+
63
+ def __init__(
64
+ self,
65
+ vocab_file,
66
+ bos_token: Union[str, AddedToken] = "[BOS]",
67
+ eos_token: Union[str, AddedToken] = "[EOS]",
68
+ unk_token: Union[str, AddedToken, None] = None,
69
+ pad_token: Union[str, AddedToken, None] = None,
70
+ additional_special_tokens: List[str] = None,
71
+ added_tokens_decoder: Optional[dict] = None,
72
+ **kwargs,
73
+ ):
74
+ assert os.path.isfile(vocab_file), vocab_file
75
+
76
+ # Transformers ≥5 may supply ``extra_special_tokens`` instead of
77
+ # ``additional_special_tokens``; treat empty dict as absent.
78
+ if additional_special_tokens is None:
79
+ extra = kwargs.pop("extra_special_tokens", None)
80
+ if isinstance(extra, dict) and not extra:
81
+ extra = None
82
+ if isinstance(extra, (list, tuple)):
83
+ additional_special_tokens = list(extra)
84
+
85
+ if additional_special_tokens is None:
86
+ additional_special_tokens = [
87
+ "<|im_end|>",
88
+ "<|im_user|>",
89
+ "<|im_assistant|>",
90
+ "<|start_header_id|>",
91
+ "<|end_header_id|>",
92
+ "[EOT]",
93
+ "<|im_system|>",
94
+ "<|im_middle|>",
95
+ ]
96
+
97
+ if added_tokens_decoder:
98
+ special_tokens_mapping = {
99
+ i: added_tokens_decoder[i].content
100
+ for i in added_tokens_decoder
101
+ }
102
+ else:
103
+ special_tokens_mapping = {}
104
+
105
+ self.vocab_file = vocab_file
106
+ mergeable_ranks = load_tiktoken_bpe(vocab_file)
107
+ num_base_tokens = len(mergeable_ranks)
108
+ self.special_tokens = {
109
+ special_tokens_mapping.get(i, f"<|reserved_token_{i}|>"): i
110
+ for i in range(num_base_tokens, num_base_tokens +
111
+ self.num_reserved_special_tokens)
112
+ }
113
+
114
+ self.model = tiktoken.Encoding(
115
+ name=Path(vocab_file).name,
116
+ pat_str=self.pat_str,
117
+ mergeable_ranks=mergeable_ranks,
118
+ special_tokens=self.special_tokens,
119
+ )
120
+ logger.info(f"Reloaded tiktoken model from {vocab_file}")
121
+
122
+ self.n_words: int = self.model.n_vocab
123
+ # BOS / EOS token IDs
124
+ self.bos_id: int = self.special_tokens[str(bos_token)]
125
+ self.eos_id: int = self.special_tokens[str(eos_token)]
126
+ logger.info(
127
+ f"#words: {self.n_words} - BOS ID: {self.bos_id} - EOS ID: {self.eos_id}"
128
+ )
129
+
130
+ self.pad_id: int = self.special_tokens[str(pad_token)]
131
+ self.unk_id: int = self.special_tokens[str(unk_token)]
132
+
133
+ self.byte_encoder = bytes_to_unicode()
134
+ self.byte_decoder = {v: k for k, v in self.byte_encoder.items()}
135
+
136
+ self.decoder = {}
137
+ for i in range(self.n_words):
138
+ # Taken from https://gist.github.com/xenova/a452a6474428de0182b17605a98631ee
139
+ decoding = ''.join([
140
+ self.byte_encoder[ord(char)] for char in
141
+ self.model.decode_single_token_bytes(i).decode('latin-1')
142
+ ])
143
+ self.decoder[i] = decoding
144
+
145
+ self.encoder = {}
146
+ for i in range(self.n_words):
147
+ if i in self.decoder:
148
+ self.encoder[self.decoder[i]] = i
149
+
150
+ self._token_config_cache = OrderedDict()
151
+ self._cache_max_size = 128
152
+
153
+ super().__init__(
154
+ bos_token=bos_token,
155
+ eos_token=eos_token,
156
+ unk_token=unk_token,
157
+ pad_token=pad_token,
158
+ additional_special_tokens=additional_special_tokens,
159
+ added_tokens_decoder=added_tokens_decoder,
160
+ **kwargs,
161
+ )
162
+ self.all_special_ids_set = set(self.all_special_ids)
163
+
164
+ def encode(self,
165
+ text: str,
166
+ allow_special_tokens: bool = True,
167
+ **kwargs) -> List[int]:
168
+ """
169
+ Encodes a string into a list of token IDs.
170
+
171
+ Args:
172
+ text (str): The input string to be encoded.
173
+
174
+ Returns:
175
+ list[int]: A list of token IDs.
176
+ """
177
+ # If there are other args, we should call super().encode because there are a lot of code
178
+ # to handle those args. supper().encode finally will call _tokenize and _convert_token_to_id.
179
+ # NOTE: our encode method is not compatible with the super().encode method,
180
+ # e.g. split_special_tokens' default is True in our encode method.
181
+ if len(kwargs) > 0:
182
+ logger.warning(f"Calling super().encode with {kwargs}")
183
+ return super().encode(text, **kwargs)
184
+
185
+ assert type(text) is str
186
+
187
+ # The tiktoken tokenizer can handle <=400k chars without
188
+ # pyo3_runtime.PanicException.
189
+ TIKTOKEN_MAX_ENCODE_CHARS = 400_000
190
+
191
+ # https://github.com/openai/tiktoken/issues/195
192
+ # Here we iterate over subsequences and split if we exceed the limit
193
+ # of max consecutive non-whitespace or whitespace characters.
194
+ MAX_NO_WHITESPACES_CHARS = 25_000
195
+
196
+ texts = self.pre_tokenizer_process(text)
197
+
198
+ all_substrs = []
199
+ for text in texts:
200
+ substrs = (
201
+ substr for i in range(0, len(text), TIKTOKEN_MAX_ENCODE_CHARS)
202
+ for substr in self._split_whitespaces_or_nonwhitespaces(
203
+ text[i:i +
204
+ TIKTOKEN_MAX_ENCODE_CHARS], MAX_NO_WHITESPACES_CHARS))
205
+ all_substrs.extend(substrs)
206
+
207
+ t: List[int] = []
208
+ for substr in all_substrs:
209
+ if allow_special_tokens:
210
+ t.extend(
211
+ # we should consider special token as a common token
212
+ self.model.encode(
213
+ substr,
214
+ allowed_special="all",
215
+ ))
216
+ else:
217
+ t.extend(
218
+ # we should consider special token as a common token
219
+ self.model.encode(
220
+ substr,
221
+ disallowed_special=(),
222
+ ))
223
+
224
+ return t
225
+
226
+ def decode(self, token_ids: Union[int, List[int]], **kwargs) -> str:
227
+ """
228
+ Decodes a list of token IDs into a string.
229
+
230
+ Args:
231
+ token_ids (List[int]): The list of token IDs to be decoded.
232
+
233
+ Returns:
234
+ str: The decoded string.
235
+ """
236
+ # If there are other args, we should call super().decode because there are a lot of code
237
+ # to handle those args. supper().encode finally will call convert_tokens_to_string and _convert_id_to_token.
238
+ if len(kwargs) > 0:
239
+ return super().decode(token_ids, **kwargs)
240
+
241
+ if type(token_ids) is int:
242
+ token_ids = [token_ids]
243
+
244
+ return self.model.decode(cast(List[int], token_ids))
245
+
246
+ @staticmethod
247
+ def _split_whitespaces_or_nonwhitespaces(
248
+ s: str, max_consecutive_slice_len: int) -> Iterator[str]:
249
+ """
250
+ Splits the string `s` so that each substring contains no more than `max_consecutive_slice_len`
251
+ consecutive whitespaces or consecutive non-whitespaces.
252
+ """
253
+ current_slice_len = 0
254
+ current_slice_is_space = s[0].isspace() if len(s) > 0 else False
255
+ slice_start = 0
256
+
257
+ for i in range(len(s)):
258
+ is_now_space = s[i].isspace()
259
+
260
+ if current_slice_is_space ^ is_now_space:
261
+ current_slice_len = 1
262
+ current_slice_is_space = is_now_space
263
+ else:
264
+ current_slice_len += 1
265
+ if current_slice_len > max_consecutive_slice_len:
266
+ yield s[slice_start:i]
267
+ slice_start = i
268
+ current_slice_len = 1
269
+ yield s[slice_start:]
270
+
271
+ def pre_tokenizer_process(self, text: str) -> List[str]:
272
+ """
273
+ pre-tokenizes the input text into a list of tokens.
274
+ This method is used to split the input text into smaller chunks for internal processing.
275
+ """
276
+ return [text]
277
+
278
+ """ ----- Below are the abstract methods required by PreTrainedTokenizer ----- """
279
+
280
+ @property
281
+ def vocab_size(self) -> int:
282
+ return self.n_words
283
+
284
+ def get_vocab(self) -> Dict[str, int]:
285
+ return self.encoder
286
+
287
+ def _tokenize(self, text: str, **kwargs) -> List[str]:
288
+ return [self.decoder[t] for t in self.encode(text)]
289
+
290
+ def _convert_token_to_id(self, token: str) -> int:
291
+ return self.encoder.get(token, self.unk_id)
292
+
293
+ def _convert_id_to_token(self, index: int) -> str:
294
+ return self.decoder.get(index)
295
+
296
+ @staticmethod
297
+ def clean_up_tokenization(out_string: str) -> str:
298
+ return out_string
299
+
300
+ def convert_tokens_to_string(self, tokens: List[str]) -> str:
301
+ text = ''.join(tokens)
302
+ text = bytearray([self.byte_decoder[c]
303
+ for c in text]).decode('utf-8', 'replace')
304
+ return text
305
+
306
+ def save_vocabulary(self,
307
+ save_directory: str,
308
+ filename_prefix: Optional[str] = None) -> Tuple[str]:
309
+ if not os.path.isdir(save_directory):
310
+ raise ValueError(
311
+ f"vocabulary path ({save_directory}) should be a directory")
312
+ out_vocab_file = os.path.join(
313
+ save_directory,
314
+ (filename_prefix + "-" if filename_prefix else "") +
315
+ VOCAB_FILES_NAMES["vocab_file"])
316
+
317
+ if os.path.abspath(self.vocab_file) != os.path.abspath(
318
+ out_vocab_file) and os.path.isfile(self.vocab_file):
319
+ copyfile(self.vocab_file, out_vocab_file)
320
+
321
+ return (out_vocab_file, )
322
+
323
+ def apply_chat_template(self,
324
+ conversation,
325
+ tools: Optional[list[dict]] = None,
326
+ tokenize: bool = False,
327
+ add_generation_prompt: bool = True,
328
+ thinking: bool = True,
329
+ preserve_thinking: bool = True,
330
+ **kwargs):
331
+
332
+ tools = deep_sort_dict(tools)
333
+
334
+ # Convert tools to TypeScript style string if tools are provided
335
+ tools_ts_str = None
336
+ if tools:
337
+ try:
338
+ tools_ts_str = encode_tools_to_typescript_style(tools)
339
+
340
+ except Exception as e:
341
+ print(f"Failed to convert tools to TypeScript style: {e}")
342
+ tools_ts_str = None
343
+
344
+ # Store the TypeScript string in kwargs so it can be accessed by the template
345
+ if tools_ts_str is not None:
346
+ kwargs['tools_ts_str'] = tools_ts_str
347
+
348
+ if not thinking:
349
+ logger.warning("thinking=False is not supported, overriding to True")
350
+ thinking = True
351
+
352
+ if not preserve_thinking:
353
+ logger.warning("preserve_thinking=False is not supported, overriding to True")
354
+ preserve_thinking = True
355
+
356
+ return super().apply_chat_template(
357
+ conversation,
358
+ tools=tools,
359
+ tokenize=tokenize,
360
+ add_generation_prompt=add_generation_prompt,
361
+ thinking=thinking,
362
+ preserve_thinking=preserve_thinking,
363
+ **kwargs)
364
+
365
+
366
+ def deep_sort_dict(obj: Any) -> Any:
367
+ if isinstance(obj, dict):
368
+ return {k: deep_sort_dict(v) for k, v in sorted(obj.items())}
369
+ if isinstance(obj, list):
370
+ return [deep_sort_dict(item) for item in obj]
371
+ return obj
tokenizer_config.json ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "163584": {
4
+ "content": "[BOS]",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "163585": {
12
+ "content": "[EOS]",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "163586": {
20
+ "content": "<|im_end|>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "163587": {
28
+ "content": "<|im_user|>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "163588": {
36
+ "content": "<|im_assistant|>",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ },
43
+ "163590": {
44
+ "content": "<|start_header_id|>",
45
+ "lstrip": false,
46
+ "normalized": false,
47
+ "rstrip": false,
48
+ "single_word": false,
49
+ "special": true
50
+ },
51
+ "163591": {
52
+ "content": "<|end_header_id|>",
53
+ "lstrip": false,
54
+ "normalized": false,
55
+ "rstrip": false,
56
+ "single_word": false,
57
+ "special": true
58
+ },
59
+ "163593": {
60
+ "content": "[EOT]",
61
+ "lstrip": false,
62
+ "normalized": false,
63
+ "rstrip": false,
64
+ "single_word": false,
65
+ "special": true
66
+ },
67
+ "163594": {
68
+ "content": "<|im_system|>",
69
+ "lstrip": false,
70
+ "normalized": false,
71
+ "rstrip": false,
72
+ "single_word": false,
73
+ "special": true
74
+ },
75
+ "163595": {
76
+ "content": "<|tool_calls_section_begin|>",
77
+ "lstrip": false,
78
+ "normalized": false,
79
+ "rstrip": false,
80
+ "single_word": false,
81
+ "special": false
82
+ },
83
+ "163596": {
84
+ "content": "<|tool_calls_section_end|>",
85
+ "lstrip": false,
86
+ "normalized": false,
87
+ "rstrip": false,
88
+ "single_word": false,
89
+ "special": false
90
+ },
91
+ "163597": {
92
+ "content": "<|tool_call_begin|>",
93
+ "lstrip": false,
94
+ "normalized": false,
95
+ "rstrip": false,
96
+ "single_word": false,
97
+ "special": false
98
+ },
99
+ "163598": {
100
+ "content": "<|tool_call_argument_begin|>",
101
+ "lstrip": false,
102
+ "normalized": false,
103
+ "rstrip": false,
104
+ "single_word": false,
105
+ "special": false
106
+ },
107
+ "163599": {
108
+ "content": "<|tool_call_end|>",
109
+ "lstrip": false,
110
+ "normalized": false,
111
+ "rstrip": false,
112
+ "single_word": false,
113
+ "special": false
114
+ },
115
+ "163601": {
116
+ "content": "<|im_middle|>",
117
+ "lstrip": false,
118
+ "normalized": false,
119
+ "rstrip": false,
120
+ "single_word": false,
121
+ "special": true
122
+ },
123
+ "163602": {
124
+ "content": "<|media_begin|>",
125
+ "lstrip": false,
126
+ "normalized": false,
127
+ "rstrip": false,
128
+ "single_word": false,
129
+ "special": true
130
+ },
131
+ "163603": {
132
+ "content": "<|media_content|>",
133
+ "lstrip": false,
134
+ "normalized": false,
135
+ "rstrip": false,
136
+ "single_word": false,
137
+ "special": true
138
+ },
139
+ "163604": {
140
+ "content": "<|media_end|>",
141
+ "lstrip": false,
142
+ "normalized": false,
143
+ "rstrip": false,
144
+ "single_word": false,
145
+ "special": true
146
+ },
147
+ "163605": {
148
+ "content": "<|media_pad|>",
149
+ "lstrip": false,
150
+ "normalized": false,
151
+ "rstrip": false,
152
+ "single_word": false,
153
+ "special": true
154
+ },
155
+ "163606": {
156
+ "content": "<think>",
157
+ "lstrip": false,
158
+ "normalized": false,
159
+ "rstrip": false,
160
+ "single_word": false,
161
+ "special": false
162
+ },
163
+ "163607": {
164
+ "content": "</think>",
165
+ "lstrip": false,
166
+ "normalized": false,
167
+ "rstrip": false,
168
+ "single_word": false,
169
+ "special": false
170
+ },
171
+ "163838": {
172
+ "content": "[UNK]",
173
+ "lstrip": false,
174
+ "normalized": false,
175
+ "rstrip": false,
176
+ "single_word": false,
177
+ "special": true
178
+ },
179
+ "163839": {
180
+ "content": "[PAD]",
181
+ "lstrip": false,
182
+ "normalized": false,
183
+ "rstrip": false,
184
+ "single_word": false,
185
+ "special": true
186
+ }
187
+ },
188
+ "auto_map": {
189
+ "AutoTokenizer": [
190
+ "tokenization_kimi.TikTokenTokenizer",
191
+ null
192
+ ]
193
+ },
194
+ "backend": "custom",
195
+ "bos_token": "[BOS]",
196
+ "clean_up_tokenization_spaces": false,
197
+ "eos_token": "[EOS]",
198
+ "extra_special_tokens": [
199
+ "<|im_end|>",
200
+ "<|im_user|>",
201
+ "<|im_assistant|>",
202
+ "<|start_header_id|>",
203
+ "<|end_header_id|>",
204
+ "[EOT]",
205
+ "<|im_system|>",
206
+ "<|im_middle|>",
207
+ "<|media_begin|>",
208
+ "<|media_content|>",
209
+ "<|media_end|>",
210
+ "<|media_pad|>"
211
+ ],
212
+ "is_local": true,
213
+ "local_files_only": false,
214
+ "model_max_length": 1000000000000000019884624838656,
215
+ "pad_token": "[PAD]",
216
+ "tokenizer_class": "TikTokenTokenizer",
217
+ "tool_parser_type": "kimi_k2",
218
+ "unk_token": "[UNK]"
219
+ }
tool_declaration_ts.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Encode structured tool declaration to typescript style string.
3
+ """
4
+ import dataclasses
5
+ import json
6
+ import logging
7
+ from collections.abc import Sequence
8
+ from typing import Any
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ _TS_INDENT = " "
13
+ _TS_FIELD_DELIMITER = ",\n"
14
+
15
+
16
+ class _SchemaRegistry:
17
+ """Registry for schema definitions to handle $ref resolution"""
18
+
19
+ def __init__(self):
20
+ self.definitions = {}
21
+ self.has_self_ref = False
22
+
23
+ def register_definitions(self, defs: dict[str, Any]):
24
+ """Register schema definitions from $defs section"""
25
+ if not defs:
26
+ return
27
+ for def_name, def_schema in defs.items():
28
+ self.definitions[def_name] = def_schema
29
+
30
+ def resolve_ref(self, ref: str) -> dict[str, Any]:
31
+ """Resolve a reference to its schema definition"""
32
+ if ref == "#":
33
+ self.has_self_ref = True
34
+ return {"$self_ref": True}
35
+ elif ref.startswith("#/$defs/"):
36
+ def_name = ref.split("/")[-1]
37
+ if def_name not in self.definitions:
38
+ raise ValueError(f"Reference not found: {ref}")
39
+ return self.definitions[def_name]
40
+ else:
41
+ raise ValueError(f"Unsupported reference format: {ref}")
42
+
43
+
44
+ def _format_description(description: str, indent: str = "") -> str:
45
+ return "\n".join([
46
+ f"{indent}// {line}" if line else ""
47
+ for line in description.split("\n")
48
+ ])
49
+
50
+
51
+ class _BaseType:
52
+ description: str
53
+ constraints: dict[str, Any]
54
+
55
+ def __init__(
56
+ self,
57
+ extra_props: dict[str, Any],
58
+ *,
59
+ allowed_constraint_keys: Sequence[str] = (),
60
+ ):
61
+ self.description = extra_props.get("description", "")
62
+ self.constraints = {
63
+ k: v
64
+ for k, v in extra_props.items() if k in allowed_constraint_keys
65
+ }
66
+
67
+ def to_typescript_style(self, indent: str = "") -> str:
68
+ raise NotImplementedError
69
+
70
+ def format_docstring(self, indent: str) -> str:
71
+ lines = []
72
+ if self.description:
73
+ lines.append(_format_description(self.description, indent))
74
+ if self.constraints:
75
+ constraints_str = ", ".join(f"{k}: {v}" for k, v in sorted(
76
+ self.constraints.items(), key=lambda kv: kv[0]))
77
+ lines.append(f"{indent}// {constraints_str}")
78
+
79
+ return "".join(x + "\n" for x in lines)
80
+
81
+
82
+ class _ParameterTypeScalar(_BaseType):
83
+ type: str
84
+
85
+ def __init__(self, type: str, extra_props: dict[str, Any] | None = None):
86
+ self.type = type
87
+
88
+ allowed_constraint_keys: list[str] = []
89
+ if self.type == "string":
90
+ allowed_constraint_keys = ["maxLength", "minLength", "pattern"]
91
+ elif self.type in ("number", "integer"):
92
+ allowed_constraint_keys = ["maximum", "minimum"]
93
+
94
+ super().__init__(extra_props or {},
95
+ allowed_constraint_keys=allowed_constraint_keys)
96
+
97
+ def to_typescript_style(self, indent: str = "") -> str:
98
+ # Map integer to number in TypeScript
99
+ if self.type == "integer":
100
+ return "number"
101
+ return self.type
102
+
103
+
104
+ class _ParameterTypeObject(_BaseType):
105
+ properties: list["_Parameter"]
106
+ additional_properties: Any | None = None
107
+
108
+ def __init__(self,
109
+ json_schema_object: dict[str, Any],
110
+ registry: _SchemaRegistry | None = None):
111
+ super().__init__(json_schema_object)
112
+
113
+ self.properties = []
114
+ self.additional_properties = None
115
+
116
+ if not json_schema_object:
117
+ return
118
+
119
+ if "$defs" in json_schema_object and registry:
120
+ registry.register_definitions(json_schema_object["$defs"])
121
+
122
+ self.additional_properties = json_schema_object.get(
123
+ "additionalProperties")
124
+ if isinstance(self.additional_properties, dict):
125
+ self.additional_properties = _parse_parameter_type(
126
+ self.additional_properties, registry)
127
+
128
+ if "properties" not in json_schema_object:
129
+ return
130
+
131
+ required_parameters = json_schema_object.get("required", [])
132
+ optional_parameters = set(
133
+ json_schema_object["properties"].keys()) - set(required_parameters)
134
+
135
+ self.properties = [
136
+ _Parameter(
137
+ name=name,
138
+ type=_parse_parameter_type(prop, registry),
139
+ optional=name in optional_parameters,
140
+ default=prop.get("default")
141
+ if isinstance(prop, dict) else None,
142
+ ) for name, prop in json_schema_object["properties"].items()
143
+ ]
144
+
145
+ def to_typescript_style(self, indent: str = "") -> str:
146
+ # sort by optional, make the required parameters first
147
+ parameters = [p for p in self.properties if not p.optional]
148
+ opt_params = [p for p in self.properties if p.optional]
149
+
150
+ parameters = sorted(parameters, key=lambda p: p.name)
151
+ parameters.extend(sorted(opt_params, key=lambda p: p.name))
152
+
153
+ param_strs = []
154
+ for p in parameters:
155
+ one = p.to_typescript_style(indent=indent + _TS_INDENT)
156
+ param_strs.append(one)
157
+
158
+ if self.additional_properties is not None:
159
+ ap_type_str = "any"
160
+ if self.additional_properties is True:
161
+ ap_type_str = "any"
162
+ elif self.additional_properties is False:
163
+ ap_type_str = "never"
164
+ elif isinstance(self.additional_properties, _ParameterType):
165
+ ap_type_str = self.additional_properties.to_typescript_style(
166
+ indent=indent + _TS_INDENT)
167
+ else:
168
+ raise ValueError(
169
+ f"Unknown additionalProperties: {self.additional_properties}"
170
+ )
171
+ param_strs.append(
172
+ f"{indent + _TS_INDENT}[k: string]: {ap_type_str}")
173
+
174
+ if not param_strs:
175
+ return "{}"
176
+
177
+ params_str = _TS_FIELD_DELIMITER.join(param_strs)
178
+ if params_str:
179
+ # add new line before and after
180
+ params_str = f"\n{params_str}\n"
181
+ # always wrap with object
182
+ return f"{{{params_str}{indent}}}"
183
+
184
+
185
+ class _ParameterTypeArray(_BaseType):
186
+ item: "_ParameterType"
187
+
188
+ def __init__(self,
189
+ json_schema_object: dict[str, Any],
190
+ registry: _SchemaRegistry | None = None):
191
+ super().__init__(json_schema_object,
192
+ allowed_constraint_keys=("minItems", "maxItems"))
193
+ if json_schema_object.get("items"):
194
+ self.item = _parse_parameter_type(json_schema_object["items"],
195
+ registry)
196
+ else:
197
+ self.item = _ParameterTypeScalar(type="any")
198
+
199
+ def to_typescript_style(self, indent: str = "") -> str:
200
+ item_docstring = self.item.format_docstring(indent + _TS_INDENT)
201
+ if item_docstring:
202
+ return ("Array<\n" + item_docstring + indent + _TS_INDENT +
203
+ self.item.to_typescript_style(indent=indent + _TS_INDENT) +
204
+ "\n" + indent + ">")
205
+ else:
206
+ return f"Array<{self.item.to_typescript_style(indent=indent)}>"
207
+
208
+
209
+ class _ParameterTypeEnum(_BaseType):
210
+ # support scalar types only
211
+ enum: list[str | int | float | bool | None]
212
+
213
+ def __init__(self, json_schema_object: dict[str, Any]):
214
+ super().__init__(json_schema_object)
215
+ self.enum = json_schema_object["enum"]
216
+
217
+ # Validate enum values against declared type if present
218
+ if "type" in json_schema_object:
219
+ typ = json_schema_object["type"]
220
+ if isinstance(typ, list):
221
+ if len(typ) == 1:
222
+ typ = typ[0]
223
+ elif len(typ) == 2:
224
+ if "null" not in typ:
225
+ raise ValueError(f"Enum type {typ} is not supported")
226
+ else:
227
+ typ = typ[0] if typ[0] != "null" else typ[1]
228
+ else:
229
+ raise ValueError(f"Enum type {typ} is not supported")
230
+ for val in self.enum:
231
+ if val is None:
232
+ continue
233
+ if typ == "string" and not isinstance(val, str):
234
+ raise ValueError(f"Enum value {val} is not a string")
235
+ elif typ == "number" and not isinstance(val, (int, float)):
236
+ raise ValueError(f"Enum value {val} is not a number")
237
+ elif typ == "integer" and not isinstance(val, int):
238
+ raise ValueError(f"Enum value {val} is not an integer")
239
+ elif typ == "boolean" and not isinstance(val, bool):
240
+ raise ValueError(f"Enum value {val} is not a boolean")
241
+
242
+ def to_typescript_style(self, indent: str = "") -> str:
243
+ return " | ".join(
244
+ [f'"{e}"' if isinstance(e, str) else str(e) for e in self.enum])
245
+
246
+
247
+ class _ParameterTypeAnyOf(_BaseType):
248
+ types: list["_ParameterType"]
249
+
250
+ def __init__(
251
+ self,
252
+ json_schema_object: dict[str, Any],
253
+ registry: _SchemaRegistry | None = None,
254
+ ):
255
+ super().__init__(json_schema_object)
256
+ self.types = [
257
+ _parse_parameter_type(t, registry)
258
+ for t in json_schema_object["anyOf"]
259
+ ]
260
+
261
+ def to_typescript_style(self, indent: str = "") -> str:
262
+ return " | ".join(
263
+ [t.to_typescript_style(indent=indent) for t in self.types])
264
+
265
+
266
+ class _ParameterTypeUnion(_BaseType):
267
+ types: list[str]
268
+
269
+ def __init__(self, json_schema_object: dict[str, Any]):
270
+ super().__init__(json_schema_object)
271
+
272
+ mapping = {
273
+ "string": "string",
274
+ "number": "number",
275
+ "integer": "number",
276
+ "boolean": "boolean",
277
+ "null": "null",
278
+ "object": "{}",
279
+ "array": "Array<any>",
280
+ }
281
+ self.types = [mapping[t] for t in json_schema_object["type"]]
282
+
283
+ def to_typescript_style(self, indent: str = "") -> str:
284
+ return " | ".join(self.types)
285
+
286
+
287
+ class _ParameterTypeRef(_BaseType):
288
+ ref_name: str
289
+ is_self_ref: bool = False
290
+
291
+ def __init__(self, json_schema_object: dict[str, Any],
292
+ registry: _SchemaRegistry):
293
+ super().__init__(json_schema_object)
294
+
295
+ ref = json_schema_object["$ref"]
296
+ resolved_schema = registry.resolve_ref(ref)
297
+
298
+ if resolved_schema.get("$self_ref", False):
299
+ self.ref_name = "parameters"
300
+ self.is_self_ref = True
301
+ else:
302
+ self.ref_name = ref.split("/")[-1]
303
+
304
+ def to_typescript_style(self, indent: str = "") -> str:
305
+ return self.ref_name
306
+
307
+
308
+ _ParameterType = (_ParameterTypeScalar
309
+ | _ParameterTypeObject
310
+ | _ParameterTypeArray
311
+ | _ParameterTypeEnum
312
+ | _ParameterTypeAnyOf
313
+ | _ParameterTypeUnion
314
+ | _ParameterTypeRef)
315
+
316
+
317
+ @dataclasses.dataclass
318
+ class _Parameter:
319
+ """
320
+ A parameter in a function, or a field in a object.
321
+ It consists of the type as well as the name.
322
+ """
323
+
324
+ type: _ParameterType
325
+ name: str = "_"
326
+ optional: bool = True
327
+ default: Any | None = None
328
+
329
+ @classmethod
330
+ def parse_extended(cls, attributes: dict[str, Any]) -> "_Parameter":
331
+ if not attributes:
332
+ raise ValueError("attributes is empty")
333
+
334
+ return cls(
335
+ name=attributes.get("name", "_"),
336
+ type=_parse_parameter_type(attributes),
337
+ optional=attributes.get("optional", False),
338
+ default=attributes.get("default"),
339
+ )
340
+
341
+ def to_typescript_style(self, indent: str = "") -> str:
342
+ comments = self.type.format_docstring(indent)
343
+
344
+ if self.default is not None:
345
+ default_repr = (json.dumps(self.default, ensure_ascii=False)
346
+ if not isinstance(self.default, (int, float, bool))
347
+ else repr(self.default))
348
+ comments += f"{indent}// Default: {default_repr}\n"
349
+
350
+ return (
351
+ comments +
352
+ f"{indent}{self.name}{'?' if self.optional else ''}: {self.type.to_typescript_style(indent=indent)}"
353
+ )
354
+
355
+
356
+ def _parse_parameter_type(
357
+ json_schema_object: dict[str, Any] | bool,
358
+ registry: _SchemaRegistry | None = None) -> _ParameterType:
359
+ if isinstance(json_schema_object, bool):
360
+ if json_schema_object:
361
+ return _ParameterTypeScalar(type="any")
362
+ else:
363
+ logger.warning(
364
+ f"Warning: Boolean value {json_schema_object} is not supported, use null instead."
365
+ )
366
+ return _ParameterTypeScalar(type="null")
367
+
368
+ if "$ref" in json_schema_object and registry:
369
+ return _ParameterTypeRef(json_schema_object, registry)
370
+
371
+ if "anyOf" in json_schema_object:
372
+ return _ParameterTypeAnyOf(json_schema_object, registry)
373
+ elif "enum" in json_schema_object:
374
+ return _ParameterTypeEnum(json_schema_object)
375
+ elif "type" in json_schema_object:
376
+ typ = json_schema_object["type"]
377
+ if isinstance(typ, list):
378
+ return _ParameterTypeUnion(json_schema_object)
379
+ elif typ == "object":
380
+ return _ParameterTypeObject(json_schema_object, registry)
381
+ elif typ == "array":
382
+ return _ParameterTypeArray(json_schema_object, registry)
383
+ else:
384
+ return _ParameterTypeScalar(typ, json_schema_object)
385
+ elif json_schema_object == {}:
386
+ return _ParameterTypeScalar(type="any")
387
+ else:
388
+ raise ValueError(f"Invalid JSON Schema object: {json_schema_object}")
389
+
390
+
391
+ def _openai_function_to_typescript_style(function: dict[str, Any], ) -> str:
392
+ """Convert OpenAI function definition (dict) to TypeScript style string."""
393
+ registry = _SchemaRegistry()
394
+ parameters = function.get("parameters") or {}
395
+ parsed = _ParameterTypeObject(parameters, registry)
396
+
397
+ interfaces = []
398
+ root_interface_name = None
399
+ if registry.has_self_ref:
400
+ root_interface_name = "parameters"
401
+ params_str = _TS_FIELD_DELIMITER.join([
402
+ p.to_typescript_style(indent=_TS_INDENT) for p in parsed.properties
403
+ ])
404
+ params_str = f"\n{params_str}\n" if params_str else ""
405
+ interface_def = f"interface {root_interface_name} {{{params_str}}}"
406
+ interfaces.append(interface_def)
407
+
408
+ definitions_copy = dict(registry.definitions)
409
+ for def_name, def_schema in definitions_copy.items():
410
+ obj_type = _parse_parameter_type(def_schema, registry)
411
+ params_str = obj_type.to_typescript_style()
412
+
413
+ description_part = ""
414
+ if obj_description := def_schema.get("description", ""):
415
+ description_part = _format_description(obj_description) + "\n"
416
+
417
+ interface_def = f"{description_part}interface {def_name} {params_str}"
418
+ interfaces.append(interface_def)
419
+
420
+ interface_str = "\n".join(interfaces)
421
+ function_name = function.get("name", "function")
422
+ if root_interface_name:
423
+ type_def = f"type {function_name} = (_: {root_interface_name}) => any;"
424
+ else:
425
+ params_str = parsed.to_typescript_style()
426
+ type_def = f"type {function_name} = (_: {params_str}) => any;"
427
+
428
+ description = function.get("description")
429
+ return "\n".join(
430
+ filter(
431
+ bool,
432
+ [
433
+ interface_str,
434
+ ((description and _format_description(description)) or ""),
435
+ type_def,
436
+ ],
437
+ ))
438
+
439
+
440
+ def encode_tools_to_typescript_style(tools: list[dict[str, Any]], ) -> str:
441
+ """
442
+ Convert tools (list of dict) to TypeScript style string.
443
+
444
+ Supports OpenAI format: {"type": "function", "function": {...}}
445
+
446
+ Args:
447
+ tools: List of tool definitions in dict format
448
+
449
+ Returns:
450
+ TypeScript style string representation of the tools
451
+ """
452
+ if not tools:
453
+ return ""
454
+
455
+ functions = []
456
+
457
+ for tool in tools:
458
+ tool_type = tool.get("type")
459
+ if tool_type == "function":
460
+ func_def = tool.get("function", {})
461
+ if func_def:
462
+ functions.append(
463
+ _openai_function_to_typescript_style(func_def))
464
+ else:
465
+ # Skip unsupported tool types (like "_plugin")
466
+ continue
467
+
468
+ if not functions:
469
+ return ""
470
+
471
+ functions_str = "\n".join(functions)
472
+ result = "# Tools\n\n"
473
+
474
+ if functions_str:
475
+ result += "## functions\nnamespace functions {\n"
476
+ result += functions_str + "\n"
477
+ result += "}\n"
478
+
479
+ return result