Kekulanus commited on
Commit
de943e4
·
verified ·
1 Parent(s): 9fe5469

Upload folder using huggingface_hub

Browse files
config.json ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "IQuestLoopCoderForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "auto_map": {
8
+ "AutoConfig": "configuration_iquestloopcoder.IQuestLoopCoderConfig",
9
+ "AutoModel": "modeling_iquestloopcoder.IQuestLoopCoderModel",
10
+ "AutoModelForCausalLM": "modeling_iquestloopcoder.IQuestLoopCoderForCausalLM"
11
+ },
12
+ "bos_token_id": 1,
13
+ "dtype": "bfloat16",
14
+ "eos_token_id": [
15
+ 75864,
16
+ 2,
17
+ 75869
18
+ ],
19
+ "head_dim": 128,
20
+ "hidden_act": "silu",
21
+ "hidden_size": 5120,
22
+ "initializer_range": 0.02,
23
+ "intermediate_size": 27648,
24
+ "loop_num": 2,
25
+ "loop_window_size": 64,
26
+ "max_position_embeddings": 16384,
27
+ "mlp_bias": false,
28
+ "model_type": "iquestloopcoder",
29
+ "num_attention_heads": 40,
30
+ "num_hidden_layers": 80,
31
+ "num_key_value_heads": 8,
32
+ "rms_norm_eps": 1e-05,
33
+ "rope_scaling": null,
34
+ "rope_theta": 500000,
35
+ "tie_word_embeddings": false,
36
+ "transformers_version": "4.56.0",
37
+ "use_cache": true,
38
+ "vocab_size": 76800,
39
+ "quantization_config": {
40
+ "config_groups": {
41
+ "group_0": {
42
+ "input_activations": {
43
+ "dynamic": false,
44
+ "num_bits": 4,
45
+ "type": "float",
46
+ "group_size": 16
47
+ },
48
+ "weights": {
49
+ "dynamic": false,
50
+ "num_bits": 4,
51
+ "type": "float",
52
+ "group_size": 16
53
+ },
54
+ "targets": [
55
+ "Linear"
56
+ ]
57
+ }
58
+ },
59
+ "ignore": [
60
+ "lm_head"
61
+ ],
62
+ "quant_algo": "NVFP4",
63
+ "producer": {
64
+ "name": "modelopt",
65
+ "version": "0.41.0"
66
+ },
67
+ "quant_method": "modelopt"
68
+ }
69
+ }
configuration_iquestloopcoder.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # IQuest NVFP4 Quantized Model - Official Architecture
2
+ # Copyright 2024 IQuestLoopCoder Authors
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ """IQuestLoopCoder model configuration"""
7
+
8
+ from transformers.configuration_utils import PretrainedConfig
9
+ from transformers.utils import logging
10
+
11
+ logger = logging.get_logger(__name__)
12
+
13
+
14
+ class IQuestLoopCoderConfig(PretrainedConfig):
15
+ r"""
16
+ Configuration class for IQuestLoopCoder model.
17
+
18
+ IQuestLoopCoder extends the standard LLaMA architecture with a loop mechanism:
19
+ - Loop 1: Standard attention, stores K1, V1
20
+ - Loop 2+: Mixed attention with gated combination of global (K1,V1) and local (K2,V2) KV
21
+
22
+ The gate is computed as: gate = sigmoid(W @ Q + bias)
23
+ Mixed output = gate * Attention(Q, K1, V1) + (1 - gate) * SlidingWindowAttention(Q, K2, V2)
24
+
25
+ Args:
26
+ vocab_size (`int`, *optional*, defaults to 76800):
27
+ Vocabulary size of the model.
28
+ hidden_size (`int`, *optional*, defaults to 5120):
29
+ Dimension of the hidden representations.
30
+ intermediate_size (`int`, *optional*, defaults to 27648):
31
+ Dimension of the MLP representations (FFN hidden size).
32
+ num_hidden_layers (`int`, *optional*, defaults to 80):
33
+ Number of hidden layers in the Transformer decoder.
34
+ num_attention_heads (`int`, *optional*, defaults to 40):
35
+ Number of attention heads for each attention layer.
36
+ num_key_value_heads (`int`, *optional*, defaults to 8):
37
+ Number of key-value heads (for GQA). If None, defaults to num_attention_heads.
38
+ head_dim (`int`, *optional*, defaults to 128):
39
+ Dimension of each attention head (hidden_size // num_attention_heads).
40
+ hidden_act (`str` or `function`, *optional*, defaults to `"silu"`):
41
+ Activation function in the MLP.
42
+ max_position_embeddings (`int`, *optional*, defaults to 8192):
43
+ Maximum sequence length.
44
+ initializer_range (`float`, *optional*, defaults to 0.02):
45
+ Standard deviation for weight initialization.
46
+ rms_norm_eps (`float`, *optional*, defaults to 1e-5):
47
+ Epsilon for RMS normalization layers.
48
+ use_cache (`bool`, *optional*, defaults to `True`):
49
+ Whether to use past key/values for generation.
50
+ tie_word_embeddings (`bool`, *optional*, defaults to `False`):
51
+ Whether to tie input and output embeddings.
52
+ rope_theta (`float`, *optional*, defaults to 500000.0):
53
+ Base value for rotary position embeddings.
54
+ attention_bias (`bool`, *optional*, defaults to `False`):
55
+ Whether to use bias in attention layers.
56
+ attention_dropout (`float`, *optional*, defaults to 0.0):
57
+ Dropout ratio for attention weights.
58
+ mlp_bias (`bool`, *optional*, defaults to `False`):
59
+ Whether to use bias in MLP layers.
60
+
61
+ # Loop-specific parameters
62
+ loop_num (`int`, *optional*, defaults to 2):
63
+ Number of loops through the decoder.
64
+ loop_window_size (`int`, *optional*, defaults to 64):
65
+ Window size for sliding window attention in Loop 2+.
66
+ """
67
+
68
+ model_type = "iquestloopcoder"
69
+ keys_to_ignore_at_inference = ["past_key_values"]
70
+
71
+ def __init__(
72
+ self,
73
+ vocab_size=76800,
74
+ hidden_size=5120,
75
+ intermediate_size=27648,
76
+ num_hidden_layers=80,
77
+ num_attention_heads=40,
78
+ num_key_value_heads=8,
79
+ head_dim=128,
80
+ hidden_act="silu",
81
+ max_position_embeddings=8192,
82
+ initializer_range=0.02,
83
+ rms_norm_eps=1e-5,
84
+ use_cache=True,
85
+ pad_token_id=None,
86
+ bos_token_id=1,
87
+ eos_token_id=2,
88
+ tie_word_embeddings=False,
89
+ rope_theta=500000.0,
90
+ rope_scaling=None,
91
+ attention_bias=False,
92
+ attention_dropout=0.0,
93
+ mlp_bias=False,
94
+ # Loop-specific parameters
95
+ loop_num=2,
96
+ loop_window_size=64,
97
+ **kwargs,
98
+ ):
99
+ self.vocab_size = vocab_size
100
+ self.max_position_embeddings = max_position_embeddings
101
+ self.hidden_size = hidden_size
102
+ self.intermediate_size = intermediate_size
103
+ self.num_hidden_layers = num_hidden_layers
104
+ self.num_attention_heads = num_attention_heads
105
+ self.head_dim = head_dim
106
+
107
+ # GQA support
108
+ if num_key_value_heads is None:
109
+ num_key_value_heads = num_attention_heads
110
+ self.num_key_value_heads = num_key_value_heads
111
+
112
+ self.hidden_act = hidden_act
113
+ self.initializer_range = initializer_range
114
+ self.rms_norm_eps = rms_norm_eps
115
+ self.use_cache = use_cache
116
+ self.rope_theta = rope_theta
117
+ self.rope_scaling = rope_scaling
118
+ self.attention_bias = attention_bias
119
+ self.attention_dropout = attention_dropout
120
+ self.mlp_bias = mlp_bias
121
+
122
+ # Loop-specific
123
+ self.loop_num = loop_num
124
+ self.loop_window_size = loop_window_size
125
+
126
+ super().__init__(
127
+ pad_token_id=pad_token_id,
128
+ bos_token_id=bos_token_id,
129
+ eos_token_id=eos_token_id,
130
+ tie_word_embeddings=tie_word_embeddings,
131
+ **kwargs,
132
+ )
133
+
generation_config.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 1,
4
+ "eos_token_id": [
5
+ 75864,
6
+ 2,
7
+ 75869
8
+ ],
9
+ "temperature": 0.3,
10
+ "top_p": 0.9,
11
+ "do_sample": true,
12
+ "transformers_version": "4.57.3"
13
+ }
model.safetensors.index.json ADDED
The diff for this file is too large to render. See raw diff
 
modeling_iquestloopcoder.py ADDED
@@ -0,0 +1,1114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # IQuest NVFP4 Quantized Model - Official Architecture
2
+ """
3
+ Modified MIT License
4
+
5
+ Software Copyright© 2025 IQuest Research
6
+
7
+ Our only modification is that, if the Software (or any derivative works
8
+ thereof) is used for any of your commercial products or services, you shall
9
+ prominently display "IQuest Coder" on the user interface of such product or
10
+ service.
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+ """
28
+
29
+ import logging
30
+ from typing import Any, Callable, Optional, Union, Tuple, List
31
+
32
+ import torch
33
+ from torch import nn
34
+
35
+ from transformers.activations import ACT2FN
36
+ from transformers.cache_utils import Cache
37
+ from transformers.generation import GenerationMixin
38
+ from transformers.integrations import use_kernel_forward_from_hub
39
+ from transformers.masking_utils import (
40
+ create_causal_mask,
41
+ create_sliding_window_causal_mask,
42
+ )
43
+ from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
44
+ from transformers.modeling_layers import (
45
+ GenericForQuestionAnswering,
46
+ GenericForSequenceClassification,
47
+ GenericForTokenClassification,
48
+ GradientCheckpointingLayer,
49
+ )
50
+ from transformers.modeling_outputs import (
51
+ BaseModelOutputWithPast,
52
+ CausalLMOutputWithPast,
53
+ )
54
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
55
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
56
+ from transformers.processing_utils import Unpack
57
+ from transformers.utils import TransformersKwargs, auto_docstring, can_return_tuple
58
+ from transformers.utils.generic import check_model_inputs
59
+ from .configuration_iquestloopcoder import IQuestLoopCoderConfig
60
+
61
+
62
+ logger = logging.getLogger(__name__)
63
+
64
+
65
+ def needs_iquestloopcoder_cache(
66
+ cache: Optional[Cache]
67
+ ) -> bool:
68
+ # need to test more conditions
69
+ if cache is None:
70
+ return True
71
+ if isinstance(cache, IQuestLoopCoderCache):
72
+ return False
73
+ return True
74
+
75
+ class IQuestLoopCoderMLP(nn.Module):
76
+ def __init__(self, config):
77
+ super().__init__()
78
+ self.config = config
79
+ self.hidden_size = config.hidden_size
80
+ self.intermediate_size = config.intermediate_size
81
+ self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
82
+ self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
83
+ self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
84
+ self.act_fn = ACT2FN[config.hidden_act]
85
+
86
+ def forward(self, x):
87
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
88
+ return down_proj
89
+
90
+
91
+ def rotate_half(x):
92
+ """Rotates half the hidden dims of the input."""
93
+ x1 = x[..., : x.shape[-1] // 2]
94
+ x2 = x[..., x.shape[-1] // 2 :]
95
+ return torch.cat((-x2, x1), dim=-1)
96
+
97
+
98
+ def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
99
+ """Applies Rotary Position Embedding to the query and key tensors.
100
+
101
+ Args:
102
+ q (`torch.Tensor`): The query tensor.
103
+ k (`torch.Tensor`): The key tensor.
104
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
105
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
106
+ position_ids (`torch.Tensor`, *optional*):
107
+ Deprecated and unused.
108
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
109
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
110
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
111
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
112
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
113
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
114
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
115
+ Returns:
116
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
117
+ """
118
+ cos = cos.unsqueeze(unsqueeze_dim)
119
+ sin = sin.unsqueeze(unsqueeze_dim)
120
+ q_embed = (q * cos) + (rotate_half(q) * sin)
121
+ k_embed = (k * cos) + (rotate_half(k) * sin)
122
+ return q_embed, k_embed
123
+
124
+
125
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
126
+ """
127
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
128
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
129
+ """
130
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
131
+ if n_rep == 1:
132
+ return hidden_states
133
+ hidden_states = hidden_states[:, :, None, :, :].expand(
134
+ batch, num_key_value_heads, n_rep, slen, head_dim
135
+ )
136
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
137
+
138
+
139
+ class IQuestLoopCoderCache(Cache):
140
+ """Cache implementation for IQuestLoopCoder that manages shared and local KV caches.
141
+
142
+ - shared_key_cache/shared_value_cache: Stores KV from Loop 1 (global context)
143
+ - local_key_cache/local_value_cache: Stores KV from Loop 2+ (local window, only window_size tokens)
144
+ """
145
+
146
+ def __init__(self, window_size: int, num_layers: int, loop_num: int=2):
147
+ # We intentionally don't call super().__init__ because the parent assumes static cache sizes.
148
+ self.window_size = window_size
149
+ self.num_layers = num_layers
150
+ self.loop_num = loop_num
151
+
152
+ # Shared cache: stores Loop 1 KV (global context)
153
+ self.shared_key_cache: List[Optional[torch.Tensor]] = [None] * self.num_layers
154
+ self.shared_value_cache: List[Optional[torch.Tensor]] = [None] * self.num_layers
155
+
156
+ # Local cache: stores Loop 2+ KV (sliding window, only window_size tokens)
157
+ self.local_key_cache: List[Optional[torch.Tensor]] = [None] * (self.loop_num-1) * self.num_layers
158
+ self.local_value_cache: List[Optional[torch.Tensor]] = [None] * (self.loop_num-1) * self.num_layers
159
+
160
+ self.layers: List[Any] = [] # attribute expected by HF Cache utilities
161
+ self._seen_tokens = 0
162
+
163
+ def update_shared(
164
+ self,
165
+ key_states: torch.Tensor,
166
+ value_states: torch.Tensor,
167
+ layer_idx: int,
168
+ cache_kwargs: Optional[dict] = None,
169
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
170
+ """Update shared cache (Loop 1 KV)."""
171
+ # only store the first loop's kv cache
172
+ loop_idx = cache_kwargs.get("loop_idx", 0)
173
+ assert loop_idx == 0
174
+ if layer_idx < 0 or layer_idx >= self.num_layers:
175
+ raise ValueError(f"layer_idx must be in [0, {self.num_layers}), got {layer_idx}")
176
+
177
+ cached_key = self.shared_key_cache[layer_idx]
178
+ cached_value = self.shared_value_cache[layer_idx]
179
+
180
+ if cached_key is None:
181
+ self.shared_key_cache[layer_idx] = key_states
182
+ self.shared_value_cache[layer_idx] = value_states
183
+ else:
184
+ if (
185
+ key_states.shape[0] != cached_key.shape[0]
186
+ or key_states.shape[1] != cached_key.shape[1]
187
+ or key_states.shape[3] != cached_key.shape[3]
188
+ ):
189
+ raise ValueError(
190
+ "Cached and incoming key/value tensors must match on batch, head, and head_dim dimensions."
191
+ )
192
+ assert key_states.shape[2] == 1
193
+ assert value_states.shape[2] == 1
194
+ self.shared_key_cache[layer_idx] = torch.cat([cached_key, key_states], dim=2)
195
+ self.shared_value_cache[layer_idx] = torch.cat([cached_value, value_states], dim=2)
196
+
197
+ result_key = self.shared_key_cache[layer_idx]
198
+ result_value = self.shared_value_cache[layer_idx]
199
+ assert result_key is not None and result_value is not None
200
+
201
+ # Track sequence length
202
+ self._seen_tokens = result_key.shape[2]
203
+ return result_key, result_value
204
+
205
+ def update_local(
206
+ self,
207
+ key_states: torch.Tensor,
208
+ value_states: torch.Tensor,
209
+ layer_idx: int,
210
+ cache_kwargs: Optional[dict] = None,
211
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
212
+ """Update local cache (Loop 2+ KV) with sliding window management.
213
+
214
+ Ensures the local cache always contains at most window_size tokens.
215
+ Local cache only stores loop_idx > 0 (i.e., loop_idx = 1, 2, ...).
216
+ For loop_idx = 1, cache_idx = layer_idx + 0 * num_layers = layer_idx (0 to num_layers-1)
217
+ For loop_idx = 2, cache_idx = layer_idx + 1 * num_layers (num_layers to 2*num_layers-1)
218
+ """
219
+ # only store the local kv cache for loop_idx > 0
220
+ loop_idx = cache_kwargs.get("loop_idx", 0)
221
+ assert loop_idx > 0, f"update_local should only be called for loop_idx > 0, got {loop_idx}"
222
+ if layer_idx < 0 or layer_idx >= self.num_layers:
223
+ raise ValueError(f"layer_idx must be in [0, {self.num_layers}), got {layer_idx}")
224
+
225
+ # Local cache size is (loop_num-1) * num_layers
226
+ # loop_idx = 1 maps to indices 0 to num_layers-1
227
+ # loop_idx = 2 maps to indices num_layers to 2*num_layers-1
228
+ # So offset = (loop_idx - 1) * num_layers
229
+ cache_idx = layer_idx + (loop_idx - 1) * self.num_layers
230
+
231
+ # Validate cache_idx is within bounds
232
+ max_cache_idx = (self.loop_num - 1) * self.num_layers
233
+ if cache_idx >= max_cache_idx:
234
+ raise IndexError(
235
+ f"cache_idx {cache_idx} out of range. "
236
+ f"loop_idx={loop_idx}, layer_idx={layer_idx}, "
237
+ f"max_cache_idx={max_cache_idx - 1}"
238
+ )
239
+ cached_key = self.local_key_cache[cache_idx]
240
+ cached_value = self.local_value_cache[cache_idx]
241
+
242
+ if cached_key is None:
243
+ # First token in local cache, for prefill
244
+ # If prefill sequence is longer than window_size, only keep the last window_size tokens
245
+ seq_len = key_states.shape[2]
246
+ if seq_len > self.window_size:
247
+ # Keep only the last window_size tokens
248
+ start_idx = seq_len - self.window_size
249
+ self.local_key_cache[cache_idx] = key_states[:, :, start_idx:, :]
250
+ self.local_value_cache[cache_idx] = value_states[:, :, start_idx:, :]
251
+ else:
252
+ self.local_key_cache[cache_idx] = key_states
253
+ self.local_value_cache[cache_idx] = value_states
254
+ else:
255
+ # store the local kv cache for decode
256
+ if (
257
+ key_states.shape[0] != cached_key.shape[0]
258
+ or key_states.shape[1] != cached_key.shape[1]
259
+ or key_states.shape[3] != cached_key.shape[3]
260
+ ):
261
+ raise ValueError(
262
+ "Cached and incoming key/value tensors must match on batch, head, and head_dim dimensions."
263
+ )
264
+ assert cached_value is not None
265
+ assert key_states.shape[2] == 1
266
+ assert value_states.shape[2] == 1
267
+ # Concatenate new tokens
268
+ new_key = torch.cat([cached_key, key_states], dim=2)
269
+ new_value = torch.cat([cached_value, value_states], dim=2)
270
+
271
+ # Ensure the total length doesn't exceed window_size
272
+ total_len = new_key.shape[2]
273
+ if total_len > self.window_size:
274
+ # Keep only the last window_size tokens
275
+ self.local_key_cache[cache_idx] = new_key[:, :, -self.window_size:, :]
276
+ self.local_value_cache[cache_idx] = new_value[:, :, -self.window_size:, :]
277
+ else:
278
+ self.local_key_cache[cache_idx] = new_key
279
+ self.local_value_cache[cache_idx] = new_value
280
+
281
+ result_key = self.local_key_cache[cache_idx]
282
+ result_value = self.local_value_cache[cache_idx]
283
+ assert result_key is not None and result_value is not None
284
+ # Ensure the result is at most window_size (can be less during prefill when sequence is shorter)
285
+ assert result_key.shape[2] <= self.window_size, f"Local cache size {result_key.shape[2]} exceeds window_size {self.window_size}"
286
+
287
+ return result_key, result_value
288
+
289
+ def get_shared(self, layer_idx: int|List[int]) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor]]:
290
+ """Get shared cache for some layer."""
291
+ if isinstance(layer_idx, list):
292
+ return [self.get_shared(layer_idx) for layer_idx in layer_idx]
293
+ if layer_idx < 0 or layer_idx >= self.num_layers:
294
+ raise ValueError(f"layer_idx must be in [0, {self.num_layers}), got {layer_idx}")
295
+ return self.shared_key_cache[layer_idx], self.shared_value_cache[layer_idx]
296
+
297
+ def get_local(self, layer_idx: int|List[int], loop_idx: int) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor]]:
298
+ """Get local cache for a layer."""
299
+ assert loop_idx > 0, f"get_local should only be called for loop_idx > 0, got {loop_idx}"
300
+ if isinstance(layer_idx, list):
301
+ return [self.get_local(layer_idx, loop_idx) for layer_idx in layer_idx]
302
+ if layer_idx < 0 or layer_idx >= self.num_layers:
303
+ raise ValueError(f"layer_idx must be in [0, {self.num_layers}), got {layer_idx}")
304
+
305
+ # Local cache size is (loop_num-1) * num_layers
306
+ # loop_idx = 1 maps to indices 0 to num_layers-1
307
+ # loop_idx = 2 maps to indices num_layers to 2*num_layers-1
308
+ # So offset = (loop_idx - 1) * num_layers
309
+ cache_idx = layer_idx + (loop_idx - 1) * self.num_layers
310
+
311
+ # Validate cache_idx is within bounds
312
+ max_cache_idx = (self.loop_num - 1) * self.num_layers
313
+ if cache_idx >= max_cache_idx:
314
+ raise IndexError(
315
+ f"cache_idx {cache_idx} out of range. "
316
+ f"loop_idx={loop_idx}, layer_idx={layer_idx}, "
317
+ f"max_cache_idx={max_cache_idx - 1}"
318
+ )
319
+
320
+ return self.local_key_cache[cache_idx], self.local_value_cache[cache_idx]
321
+
322
+ def update(
323
+ self,
324
+ key_states: torch.Tensor,
325
+ value_states: torch.Tensor,
326
+ layer_idx: int,
327
+ cache_kwargs: Optional[dict] = None,
328
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
329
+ """Default update method (for compatibility, updates shared cache)."""
330
+ loop_idx = cache_kwargs.get("loop_idx", 0)
331
+ assert loop_idx < self.loop_num
332
+ if loop_idx == 0:
333
+ return self.update_shared(key_states, value_states, layer_idx, cache_kwargs)
334
+ else:
335
+ return self.update_local(key_states, value_states, layer_idx, cache_kwargs)
336
+
337
+ def get_seq_length(self, layer_idx: Optional[int] = 0) -> int:
338
+ """Get sequence length from shared cache."""
339
+ if layer_idx is None:
340
+ layer_idx = 0
341
+ if layer_idx < 0 or layer_idx >= self.loop_num * self.num_layers:
342
+ return 0
343
+ cached_key = self.shared_key_cache[layer_idx]
344
+ if cached_key is None:
345
+ return 0
346
+ return cached_key.shape[2]
347
+
348
+ def get_max_length(self) -> Optional[int]:
349
+ return None
350
+
351
+ def get_usable_length(
352
+ self, new_seq_length: int, layer_idx: Optional[int] = 0
353
+ ) -> int:
354
+ return self.get_seq_length(layer_idx)
355
+
356
+ def reorder_cache(self, beam_idx: torch.LongTensor) -> None:
357
+ # pass
358
+ raise NotImplementedError("Reorder cache for beam search is not implemented")
359
+ """Reorder cache for beam search.
360
+
361
+ Reorders both shared cache (Loop 1) and local cache (Loop 2+) according to beam_idx.
362
+ """
363
+ # Reorder shared cache (Loop 1, loop_idx=0)
364
+ for layer_idx in range(self.num_layers):
365
+ if self.shared_key_cache[layer_idx] is not None:
366
+ device = self.shared_key_cache[layer_idx].device
367
+ self.shared_key_cache[layer_idx] = self.shared_key_cache[layer_idx].index_select(0, beam_idx.to(device))
368
+ self.shared_value_cache[layer_idx] = self.shared_value_cache[layer_idx].index_select(0, beam_idx.to(device))
369
+
370
+ # Reorder local cache (Loop 2+, loop_idx > 0)
371
+ # Local cache size is (loop_num-1) * num_layers
372
+ for cache_idx in range(len(self.local_key_cache)):
373
+ if self.local_key_cache[cache_idx] is not None:
374
+ device = self.local_key_cache[cache_idx].device
375
+ self.local_key_cache[cache_idx] = self.local_key_cache[cache_idx].index_select(0, beam_idx.to(device))
376
+ self.local_value_cache[cache_idx] = self.local_value_cache[cache_idx].index_select(0, beam_idx.to(device))
377
+
378
+ @property
379
+ def is_compileable(self) -> bool:
380
+ return False
381
+
382
+ def clear(self) -> None:
383
+ """Clear all caches."""
384
+ logger.debug("Clearing IQuestLoopCoderCache")
385
+ self.shared_key_cache = [None] * self.num_layers
386
+ self.shared_value_cache = [None] * self.num_layers
387
+ self.local_key_cache = [None] * self.num_layers * (self.loop_num-1)
388
+ self.local_value_cache = [None] * self.num_layers * (self.loop_num-1)
389
+ self._seen_tokens = 0
390
+
391
+
392
+ def eager_attention_forward(
393
+ module: nn.Module,
394
+ query: torch.Tensor,
395
+ key: torch.Tensor,
396
+ value: torch.Tensor,
397
+ attention_mask: Optional[torch.Tensor],
398
+ scaling: float,
399
+ dropout: float = 0.0,
400
+ **kwargs: Unpack[TransformersKwargs],
401
+ ):
402
+ key_states = repeat_kv(key, module.num_key_value_groups)
403
+ value_states = repeat_kv(value, module.num_key_value_groups)
404
+
405
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
406
+ if attention_mask is not None:
407
+ causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
408
+ attn_weights = attn_weights + causal_mask
409
+
410
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(
411
+ query.dtype
412
+ )
413
+ attn_weights = nn.functional.dropout(
414
+ attn_weights, p=dropout, training=module.training
415
+ )
416
+ attn_output = torch.matmul(attn_weights, value_states)
417
+ attn_output = attn_output.transpose(1, 2).contiguous()
418
+
419
+ return attn_output, attn_weights
420
+
421
+ class LoopGateProjection(nn.Module):
422
+ """Gate projection for mixed attention in Loop 2+.
423
+
424
+ Computes: g = sigmoid(linear(Q)) for each head independently.
425
+ This gate determines how much to use Loop1's KV (global) vs current loop's KV (local).
426
+ """
427
+
428
+ def __init__(self, num_heads: int, head_dim: int):
429
+ super().__init__()
430
+ self.num_heads = num_heads
431
+ self.head_dim = head_dim
432
+ # Each head has its own gate: Linear(head_dim -> 1) per head
433
+ # Implemented as [num_heads, head_dim] weight + [num_heads] bias
434
+ self.weight = nn.Parameter(torch.zeros(num_heads, head_dim))
435
+ self.bias = nn.Parameter(torch.zeros(num_heads))
436
+
437
+ def forward(self, query: torch.Tensor) -> torch.Tensor:
438
+ """Compute gate values from query tensor.
439
+
440
+ Args:
441
+ query: [batch, num_heads, seq_len, head_dim]
442
+
443
+ Returns:
444
+ gate: [batch, num_heads, seq_len, 1]
445
+ """
446
+ # query: [batch, num_heads, seq_len, head_dim]
447
+ # weight: [num_heads, head_dim]
448
+ # For each head h: gate_h = query[:, h, :, :] @ weight[h, :].T + bias[h]
449
+ # Using einsum: gate = einsum('bhsd,hd->bhs', query, weight) + bias
450
+ gate_logits = torch.einsum('bhsd,hd->bhs', query, self.weight) # [batch, num_heads, seq_len]
451
+ gate_logits = gate_logits + self.bias[None, :, None] # broadcast bias
452
+ gate = torch.sigmoid(gate_logits)
453
+ return gate.unsqueeze(-1) # [batch, num_heads, seq_len, 1]
454
+
455
+ class IQuestLoopCoderAttention(nn.Module):
456
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
457
+
458
+ def __init__(self, config: IQuestLoopCoderConfig, layer_idx: int):
459
+ super().__init__()
460
+ self.config = config
461
+ assert layer_idx >= 0 and layer_idx < config.num_hidden_layers
462
+ self.layer_idx = layer_idx
463
+
464
+ self.head_dim = getattr(
465
+ config, "head_dim", config.hidden_size // config.num_attention_heads
466
+ )
467
+ self.num_key_value_groups = (
468
+ config.num_attention_heads // config.num_key_value_heads
469
+ )
470
+ self.scaling = self.head_dim**-0.5
471
+ self.attention_dropout = config.attention_dropout
472
+ self.is_causal = True
473
+ self.q_proj = nn.Linear(
474
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=False
475
+ )
476
+ self.k_proj = nn.Linear(
477
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False
478
+ )
479
+ self.v_proj = nn.Linear(
480
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False
481
+ )
482
+ self.o_proj = nn.Linear(
483
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=False
484
+ )
485
+
486
+ def forward(
487
+ self,
488
+ hidden_states: torch.Tensor,
489
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
490
+ attention_mask: Optional[torch.Tensor],
491
+ past_key_value: Optional[Cache] = None,
492
+ cache_position: Optional[torch.LongTensor] = None,
493
+ loop_idx: int = 0,
494
+ gate_proj: Optional[LoopGateProjection] = None,
495
+ **kwargs: Unpack[FlashAttentionKwargs],
496
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor], Optional[tuple[torch.Tensor]]]:
497
+ if loop_idx == 0:
498
+ return self.forward_loop1(hidden_states, loop_idx, position_embeddings, attention_mask, past_key_value, cache_position, **kwargs)
499
+ else:
500
+ return self.forward_loop2(hidden_states, loop_idx, position_embeddings, attention_mask, past_key_value, cache_position, gate_proj, **kwargs)
501
+
502
+ def forward_loop1(
503
+ self,
504
+ hidden_states: torch.Tensor,
505
+ loop_idx: int,
506
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
507
+ attention_mask: Optional[torch.Tensor],
508
+ past_key_value: Optional[IQuestLoopCoderCache] = None,
509
+ cache_position: Optional[torch.LongTensor] = None,
510
+ **kwargs: Unpack[FlashAttentionKwargs]) -> tuple[torch.Tensor, Optional[torch.Tensor], Optional[tuple[torch.Tensor]]]:
511
+ input_shape = hidden_states.shape[:-1]
512
+ hidden_shape = (*input_shape, -1, self.head_dim)
513
+
514
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
515
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
516
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
517
+
518
+ cos, sin = position_embeddings
519
+ query_states, key_states = apply_rotary_pos_emb(
520
+ query_states, key_states, cos, sin
521
+ )
522
+
523
+ if past_key_value is not None:
524
+ # sin and cos are specific to RoPE models; cache_position needed for the static cache
525
+ cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position, "loop_idx": loop_idx}
526
+ key_states, value_states = past_key_value.update(
527
+ key_states,
528
+ value_states,
529
+ self.layer_idx,
530
+ cache_kwargs,
531
+ )
532
+
533
+ attention_interface: Callable = eager_attention_forward
534
+ if self.config._attn_implementation != "eager":
535
+ attention_interface = ALL_ATTENTION_FUNCTIONS[
536
+ self.config._attn_implementation
537
+ ]
538
+
539
+ attn_output, attn_weights = attention_interface(
540
+ self,
541
+ query_states,
542
+ key_states,
543
+ value_states,
544
+ attention_mask,
545
+ dropout=0.0 if not self.training else self.attention_dropout,
546
+ scaling=self.scaling,
547
+ **kwargs,
548
+ )
549
+
550
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
551
+ attn_output = self.o_proj(attn_output)
552
+ return attn_output, (attn_weights)
553
+
554
+
555
+ def forward_loop2(
556
+ self,
557
+ hidden_states: torch.Tensor,
558
+ loop_idx: int,
559
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
560
+ attention_mask: Optional[torch.Tensor],
561
+ past_key_value: Optional[IQuestLoopCoderCache] = None,
562
+ cache_position: Optional[torch.LongTensor] = None,
563
+ gate_proj: Optional[LoopGateProjection] = None,
564
+ **kwargs: Unpack[FlashAttentionKwargs]) -> tuple[torch.Tensor, Optional[torch.Tensor], Optional[tuple[torch.Tensor]]]:
565
+
566
+ input_shape = hidden_states.shape[:-1]
567
+ hidden_shape = (*input_shape, -1, self.head_dim)
568
+
569
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
570
+ key_states_local = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
571
+ value_states_local = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
572
+
573
+ cos, sin = position_embeddings
574
+ query_states, key_states_local = apply_rotary_pos_emb(
575
+ query_states, key_states_local, cos, sin
576
+ )
577
+
578
+ key_states_share, value_states_share = None, None
579
+ if past_key_value is not None:
580
+ # get key_share, value_share from past_key_value
581
+ cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position, "loop_idx": loop_idx}
582
+ key_states_share, value_states_share = past_key_value.get_shared(self.layer_idx)
583
+ key_states_local, value_states_local = past_key_value.update(
584
+ key_states_local,
585
+ value_states_local,
586
+ self.layer_idx,
587
+ cache_kwargs,
588
+ )
589
+
590
+ attention_interface: Callable = eager_attention_forward
591
+ if self.config._attn_implementation != "eager":
592
+ attention_interface = ALL_ATTENTION_FUNCTIONS[
593
+ self.config._attn_implementation
594
+ ]
595
+
596
+ # Create masks for global and local attention
597
+ # Global attention: full causal mask (can see all tokens in shared cache)
598
+ # Local attention: causal mask for local window (can only see window_size tokens in local cache)
599
+ attention_mask_global = attention_mask # Use full causal mask for global attention
600
+
601
+ # For local attention, create a mask that matches the local cache size
602
+ # The local cache already contains only the last window_size tokens,
603
+ # so we need a causal mask that allows attention within this window
604
+ attention_mask_local = None
605
+ if key_states_local is not None and value_states_local is not None:
606
+ # Local cache has shape [batch, num_heads, local_seq_len, head_dim]
607
+ # where local_seq_len <= window_size
608
+ local_seq_len = key_states_local.shape[2]
609
+ bsz = query_states.shape[0]
610
+ q_len = query_states.shape[2]
611
+
612
+ # Create a causal mask for local attention
613
+ # This allows each query position to attend to all positions up to and including itself
614
+ # within the local window (which is already the last window_size tokens)
615
+ device = query_states.device
616
+ dtype = query_states.dtype
617
+
618
+ if attention_mask is not None:
619
+ # If we have a global mask, we need to adapt it for local attention
620
+ # The global mask shape is [batch, 1, q_len, global_kv_len]
621
+ # For local attention, we only need the last local_seq_len positions
622
+ global_kv_len = attention_mask.shape[-1]
623
+
624
+ if global_kv_len >= local_seq_len:
625
+ # Extract the last local_seq_len columns from the global mask
626
+ # This represents attention to the last window_size tokens
627
+ attention_mask_local = attention_mask[..., -local_seq_len:]
628
+ else:
629
+ # If global mask is shorter than local_seq_len, create a simple causal mask
630
+ # This can happen during prefill when local cache is being built
631
+ attention_mask_local = torch.triu(
632
+ torch.ones((q_len, local_seq_len), device=device, dtype=dtype) * float("-inf"),
633
+ diagonal=1
634
+ ).unsqueeze(0).expand(bsz, -1, -1, -1) # [batch, 1, q_len, local_seq_len]
635
+ else:
636
+ # No global mask provided, create a simple causal mask for local attention
637
+ # This allows full attention within the local window (causal)
638
+ attention_mask_local = torch.triu(
639
+ torch.ones((q_len, local_seq_len), device=device, dtype=dtype) * float("-inf"),
640
+ diagonal=1
641
+ ).unsqueeze(0).expand(bsz, -1, -1, -1) # [batch, 1, q_len, local_seq_len]
642
+
643
+ # global attn: attend to all tokens in shared cache
644
+ attn_output_global, attn_weights_global = attention_interface(
645
+ self,
646
+ query_states,
647
+ key_states_share,
648
+ value_states_share,
649
+ attention_mask_global,
650
+ dropout=0.0 if not self.training else self.attention_dropout,
651
+ scaling=self.scaling,
652
+ **kwargs,
653
+ )
654
+
655
+ # local attn: attend only to tokens in local cache (window_size)
656
+ attn_output_local, attn_weights_local = attention_interface(
657
+ self,
658
+ query_states,
659
+ key_states_local,
660
+ value_states_local,
661
+ attention_mask_local,
662
+ dropout=0.0 if not self.training else self.attention_dropout,
663
+ scaling=self.scaling,
664
+ **kwargs,
665
+ )
666
+
667
+ # attention_interface returns [batch, seq_len, num_heads, head_dim] for eager_attention_forward
668
+ # but Flash Attention might return [batch, num_heads, seq_len, head_dim]
669
+ # We need [batch, num_heads, seq_len, head_dim] to match gate shape
670
+ q_len = query_states.shape[2] # Query sequence length
671
+ num_heads = query_states.shape[1]
672
+
673
+ # Normalize attn_output_global to [batch, num_heads, q_len, head_dim]
674
+ if attn_output_global.dim() == 4:
675
+ # Check if shape is [batch, seq_len, num_heads, head_dim] (eager) or [batch, num_heads, seq_len, head_dim] (flash)
676
+ if attn_output_global.shape[1] == q_len:
677
+ # Shape is [batch, seq_len, num_heads, head_dim], transpose to [batch, num_heads, seq_len, head_dim]
678
+ attn_output_global = attn_output_global.transpose(1, 2)
679
+ # Ensure sequence length matches query length (take first q_len tokens)
680
+ if attn_output_global.shape[2] > q_len:
681
+ attn_output_global = attn_output_global[:, :, :q_len, :]
682
+ elif attn_output_global.shape[2] < q_len:
683
+ # This shouldn't happen, but handle it gracefully
684
+ raise ValueError(f"attn_output_global seq_len {attn_output_global.shape[2]} < q_len {q_len}")
685
+
686
+ # Normalize attn_output_local to [batch, num_heads, q_len, head_dim]
687
+ if attn_output_local.dim() == 4:
688
+ # Check if shape is [batch, seq_len, num_heads, head_dim] (eager) or [batch, num_heads, seq_len, head_dim] (flash)
689
+ if attn_output_local.shape[1] == q_len:
690
+ # Shape is [batch, seq_len, num_heads, head_dim], transpose to [batch, num_heads, seq_len, head_dim]
691
+ attn_output_local = attn_output_local.transpose(1, 2)
692
+ # Ensure sequence length matches query length (take first q_len tokens)
693
+ if attn_output_local.shape[2] > q_len:
694
+ attn_output_local = attn_output_local[:, :, :q_len, :]
695
+ elif attn_output_local.shape[2] < q_len:
696
+ # This shouldn't happen, but handle it gracefully
697
+ raise ValueError(f"attn_output_local seq_len {attn_output_local.shape[2]} < q_len {q_len}")
698
+
699
+ assert gate_proj is not None
700
+ gate = gate_proj(query_states) # [batch, num_heads, seq_len, 1]
701
+ mixed_attn_output = attn_output_local * (1 - gate) + attn_output_global * gate
702
+
703
+ mixed_attn_output = mixed_attn_output.reshape(*input_shape, -1).contiguous()
704
+ mixed_attn_output = self.o_proj(mixed_attn_output)
705
+ return mixed_attn_output, (attn_weights_global, attn_weights_local, attn_output_global, attn_output_local, gate)
706
+
707
+
708
+ @use_kernel_forward_from_hub("RMSNorm")
709
+ class IQuestLoopCoderRMSNorm(nn.Module):
710
+ def __init__(self, hidden_size, eps=1e-6):
711
+ """
712
+ IQuestLoopCoderRMSNorm is equivalent to T5LayerNorm
713
+ """
714
+ super().__init__()
715
+ self.weight = nn.Parameter(torch.ones(hidden_size))
716
+ self.variance_epsilon = eps
717
+
718
+ def forward(self, hidden_states):
719
+ input_dtype = hidden_states.dtype
720
+ hidden_states = hidden_states.to(torch.float32)
721
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
722
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
723
+ return self.weight * hidden_states.to(input_dtype)
724
+
725
+ def extra_repr(self):
726
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
727
+
728
+
729
+ class IQuestLoopCoderDecoderLayer(GradientCheckpointingLayer):
730
+ def __init__(self, config: IQuestLoopCoderConfig, layer_idx: int):
731
+ super().__init__()
732
+ self.hidden_size = config.hidden_size
733
+
734
+ self.self_attn = IQuestLoopCoderAttention(config=config, layer_idx=layer_idx)
735
+
736
+ self.mlp = IQuestLoopCoderMLP(config)
737
+ self.input_layernorm = IQuestLoopCoderRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
738
+ self.post_attention_layernorm = IQuestLoopCoderRMSNorm(
739
+ config.hidden_size, eps=config.rms_norm_eps
740
+ )
741
+ self.layer_idx = layer_idx
742
+
743
+ def forward(
744
+ self,
745
+ hidden_states: torch.Tensor,
746
+ loop_idx: int = 0,
747
+ gate_proj: Optional[LoopGateProjection] = None,
748
+ attention_mask: Optional[torch.Tensor] = None,
749
+ position_ids: Optional[torch.LongTensor] = None,
750
+ past_key_value: Optional[Cache] = None,
751
+ use_cache: Optional[bool] = False,
752
+ cache_position: Optional[torch.LongTensor] = None,
753
+ position_embeddings: Optional[
754
+ tuple[torch.Tensor, torch.Tensor]
755
+ ] = None, # necessary, but kept here for BC
756
+ **kwargs: Unpack[TransformersKwargs],
757
+ ) -> tuple[torch.Tensor]:
758
+ residual = hidden_states
759
+ hidden_states = self.input_layernorm(hidden_states)
760
+ # Self Attention
761
+ hidden_states, _ = self.self_attn(
762
+ hidden_states=hidden_states,
763
+ attention_mask=attention_mask,
764
+ position_ids=position_ids,
765
+ past_key_value=past_key_value,
766
+ use_cache=use_cache,
767
+ cache_position=cache_position,
768
+ loop_idx=loop_idx,
769
+ position_embeddings=position_embeddings,
770
+ gate_proj=gate_proj if loop_idx > 0 else None,
771
+ **kwargs,
772
+ )
773
+
774
+ hidden_states = residual + hidden_states
775
+
776
+ # Fully Connected
777
+ residual = hidden_states
778
+ hidden_states = self.post_attention_layernorm(hidden_states)
779
+ hidden_states = self.mlp(hidden_states)
780
+ hidden_states = residual + hidden_states
781
+ return hidden_states
782
+
783
+
784
+ @auto_docstring
785
+ class IQuestLoopCoderPreTrainedModel(PreTrainedModel):
786
+ config: IQuestLoopCoderConfig
787
+ base_model_prefix = "model"
788
+ supports_gradient_checkpointing = True
789
+ _no_split_modules = ["IQuestLoopCoderDecoderLayer"]
790
+ _skip_keys_device_placement = ["past_key_values"]
791
+ _supports_flash_attn = True
792
+ _supports_sdpa = True
793
+ _supports_flex_attn = True
794
+
795
+ _can_compile_fullgraph = True
796
+ _supports_attention_backend = True
797
+ _can_record_outputs = {
798
+ "hidden_states": IQuestLoopCoderDecoderLayer,
799
+ "attentions": IQuestLoopCoderAttention,
800
+ }
801
+
802
+ # Important for inference with `device_map` / low_cpu_mem_usage:
803
+ # Avoid initializing parameters that are not present in the checkpoint.
804
+ # Those should keep their constructor-time initialization (e.g. zeros for LoopGateProjection),
805
+ # instead of being materialized from meta/empty tensors which can contain NaNs.
806
+ def _init_weights(self, module: nn.Module) -> None:
807
+ return
808
+
809
+
810
+ class IQuestLoopCoderRotaryEmbedding(nn.Module):
811
+ def __init__(self, config: IQuestLoopCoderConfig, device=None):
812
+ super().__init__()
813
+ # BC: "rope_type" was originally "type"
814
+ if hasattr(config, "rope_scaling") and isinstance(config.rope_scaling, dict):
815
+ self.rope_type = config.rope_scaling.get(
816
+ "rope_type", config.rope_scaling.get("type")
817
+ )
818
+ else:
819
+ self.rope_type = "default"
820
+ self.max_seq_len_cached = config.max_position_embeddings
821
+ self.original_max_seq_len = config.max_position_embeddings
822
+
823
+ self.config = config
824
+ self.rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
825
+
826
+ inv_freq, self.attention_scaling = self.rope_init_fn(self.config, device)
827
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
828
+ self.original_inv_freq = self.inv_freq
829
+
830
+ @torch.no_grad()
831
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
832
+ def forward(self, x, position_ids):
833
+ inv_freq_expanded = (
834
+ self.inv_freq[None, :, None]
835
+ .float()
836
+ .expand(position_ids.shape[0], -1, 1)
837
+ .to(x.device)
838
+ )
839
+ position_ids_expanded = position_ids[:, None, :].float()
840
+
841
+ device_type = (
842
+ x.device.type
843
+ if isinstance(x.device.type, str) and x.device.type != "mps"
844
+ else "cpu"
845
+ )
846
+ with torch.autocast(device_type=device_type, enabled=False): # Force float32
847
+ freqs = (
848
+ inv_freq_expanded.float() @ position_ids_expanded.float()
849
+ ).transpose(1, 2)
850
+ emb = torch.cat((freqs, freqs), dim=-1)
851
+ cos = emb.cos() * self.attention_scaling
852
+ sin = emb.sin() * self.attention_scaling
853
+
854
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
855
+
856
+
857
+ @auto_docstring
858
+ class IQuestLoopCoderModel(IQuestLoopCoderPreTrainedModel):
859
+ def __init__(self, config: IQuestLoopCoderConfig):
860
+ super().__init__(config)
861
+ self.padding_idx = config.pad_token_id
862
+ self.vocab_size = config.vocab_size
863
+
864
+ self.embed_tokens = nn.Embedding(
865
+ config.vocab_size, config.hidden_size, self.padding_idx
866
+ )
867
+ self.layers = nn.ModuleList(
868
+ [
869
+ IQuestLoopCoderDecoderLayer(config, layer_idx)
870
+ for layer_idx in range(config.num_hidden_layers)
871
+ ]
872
+ )
873
+ self.norm = IQuestLoopCoderRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
874
+ self.rotary_emb = IQuestLoopCoderRotaryEmbedding(config=config)
875
+ self.gradient_checkpointing = False
876
+ self.loop_num = getattr(self.config, "loop_num", 2)
877
+ self.loop_window_size = getattr(self.config, "loop_window_size", 64)
878
+
879
+ # Gate projections for Loop 2+ (one per layer)
880
+ self.gate_projections = nn.ModuleList([
881
+ LoopGateProjection(config.num_attention_heads, config.head_dim)
882
+ for _ in range(config.num_hidden_layers)
883
+ ])
884
+
885
+ # Initialize weights and apply final processing
886
+ self.post_init()
887
+
888
+ @check_model_inputs
889
+ @auto_docstring
890
+ def forward(
891
+ self,
892
+ input_ids: Optional[torch.LongTensor] = None,
893
+ attention_mask: Optional[torch.Tensor] = None,
894
+ position_ids: Optional[torch.LongTensor] = None,
895
+ past_key_values: Optional[Cache] = None,
896
+ inputs_embeds: Optional[torch.FloatTensor] = None,
897
+ use_cache: Optional[bool] = None,
898
+ cache_position: Optional[torch.LongTensor] = None,
899
+ **kwargs: Unpack[TransformersKwargs],
900
+ ) -> BaseModelOutputWithPast:
901
+
902
+ if (input_ids is None) ^ (inputs_embeds is not None):
903
+ raise ValueError(
904
+ "You must specify exactly one of input_ids or inputs_embeds"
905
+ )
906
+
907
+ if inputs_embeds is None:
908
+ inputs_embeds = self.embed_tokens(input_ids)
909
+
910
+ if use_cache is None:
911
+ use_cache = self.config.use_cache
912
+
913
+ if use_cache:
914
+ if needs_iquestloopcoder_cache(past_key_values):
915
+ past_key_values = IQuestLoopCoderCache(self.loop_window_size, self.config.num_hidden_layers, self.loop_num)
916
+
917
+ if cache_position is None:
918
+ past_seen_tokens = (
919
+ past_key_values.get_seq_length() if past_key_values is not None else 0
920
+ )
921
+ cache_position = torch.arange(
922
+ past_seen_tokens,
923
+ past_seen_tokens + inputs_embeds.shape[1],
924
+ device=inputs_embeds.device,
925
+ )
926
+
927
+ if position_ids is None:
928
+ position_ids = cache_position.unsqueeze(0)
929
+
930
+ # It may already have been prepared by e.g. `generate`
931
+ if not isinstance(causal_mask_mapping := attention_mask, dict):
932
+ # Prepare mask arguments
933
+ mask_kwargs = {
934
+ "config": self.config,
935
+ "input_embeds": inputs_embeds,
936
+ "attention_mask": attention_mask,
937
+ "cache_position": cache_position,
938
+ "past_key_values": past_key_values,
939
+ "position_ids": position_ids,
940
+ }
941
+ # Create the full causal mask for all layers
942
+ # All layers use full_attention (no sliding window layers)
943
+ full_attention_mask = create_causal_mask(**mask_kwargs)
944
+ causal_mask_mapping = {
945
+ "full_attention": full_attention_mask,
946
+ }
947
+
948
+ hidden_states = inputs_embeds
949
+
950
+ # create position embeddings to be shared across the decoder layers
951
+ position_embeddings = self.rotary_emb(hidden_states, position_ids)
952
+ hidden_states_list = []
953
+
954
+ for loop_idx in range(self.loop_num):
955
+ # For each loop, use the full_attention mask
956
+ # Loop 1: uses full_attention mask directly
957
+ # Loop 2+: forward_loop2 will create local mask internally, but uses full_attention mask for global attention
958
+ loop_attention_mask = causal_mask_mapping["full_attention"]
959
+
960
+ for layer_idx, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
961
+ hidden_states = decoder_layer(
962
+ hidden_states,
963
+ loop_idx,
964
+ gate_proj=self.gate_projections[layer_idx] if loop_idx > 0 else None,
965
+ attention_mask=loop_attention_mask,
966
+ position_ids=position_ids,
967
+ past_key_value=past_key_values,
968
+ use_cache=use_cache,
969
+ cache_position=cache_position,
970
+ position_embeddings=position_embeddings,
971
+ **kwargs,
972
+ )
973
+ if loop_idx < self.loop_num - 1:
974
+ hidden_states_list.append(hidden_states)
975
+
976
+ hidden_states = self.norm(hidden_states)
977
+ hidden_states_list.append(hidden_states)
978
+
979
+ return (
980
+ BaseModelOutputWithPast(
981
+ last_hidden_state=hidden_states,
982
+ past_key_values=past_key_values if use_cache else None,
983
+ ),
984
+ hidden_states_list,
985
+ )
986
+
987
+
988
+ @auto_docstring
989
+ class IQuestLoopCoderForCausalLM(IQuestLoopCoderPreTrainedModel, GenerationMixin):
990
+ _tied_weights_keys = ["lm_head.weight"]
991
+ _tp_plan = {"lm_head": "colwise_rep"}
992
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
993
+
994
+ def __init__(self, config):
995
+ super().__init__(config)
996
+ self.model = IQuestLoopCoderModel(config)
997
+ self.vocab_size = config.vocab_size
998
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
999
+
1000
+ # 分块大小配置
1001
+ self.chunk_size = getattr(config, "chunk_size", 2) # 默认分块大小为2
1002
+
1003
+ self.post_init()
1004
+
1005
+ def get_input_embeddings(self):
1006
+ return self.model.embed_tokens
1007
+
1008
+ def set_input_embeddings(self, value):
1009
+ self.model.embed_tokens = value
1010
+
1011
+ def get_output_embeddings(self):
1012
+ return self.lm_head
1013
+
1014
+ def set_output_embeddings(self, new_embeddings):
1015
+ self.lm_head = new_embeddings
1016
+
1017
+ def set_decoder(self, decoder):
1018
+ self.model = decoder
1019
+
1020
+ def get_decoder(self):
1021
+ return self.model
1022
+
1023
+ @can_return_tuple
1024
+ @auto_docstring
1025
+ def forward(
1026
+ self,
1027
+ input_ids: Optional[torch.LongTensor] = None,
1028
+ attention_mask: Optional[torch.Tensor] = None,
1029
+ position_ids: Optional[torch.LongTensor] = None,
1030
+ past_key_values: Optional[Cache] = None,
1031
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1032
+ labels: Optional[torch.LongTensor] = None,
1033
+ use_cache: Optional[bool] = None,
1034
+ cache_position: Optional[torch.LongTensor] = None,
1035
+ logits_to_keep: Union[int, torch.Tensor] = 0,
1036
+ **kwargs: Unpack[TransformersKwargs],
1037
+ ) -> CausalLMOutputWithPast:
1038
+
1039
+ outputs, hidden_states_list = self.model(
1040
+ input_ids=input_ids,
1041
+ attention_mask=attention_mask,
1042
+ position_ids=position_ids,
1043
+ past_key_values=past_key_values,
1044
+ inputs_embeds=inputs_embeds,
1045
+ use_cache=use_cache,
1046
+ cache_position=cache_position,
1047
+ **kwargs,
1048
+ )
1049
+ slice_indices = (
1050
+ slice(-logits_to_keep, None)
1051
+ if isinstance(logits_to_keep, int)
1052
+ else logits_to_keep
1053
+ )
1054
+
1055
+ def _select_token_positions(tensor: torch.Tensor) -> torch.Tensor:
1056
+ if isinstance(slice_indices, slice):
1057
+ return tensor[:, slice_indices, ...]
1058
+ if isinstance(slice_indices, torch.Tensor):
1059
+ return tensor.index_select(1, slice_indices.to(tensor.device))
1060
+ raise TypeError(
1061
+ f"Unsupported index type for logits_to_keep: {type(slice_indices)}"
1062
+ )
1063
+
1064
+ stacked_exit_pdf = None
1065
+
1066
+ expected_logits_cache: Optional[torch.Tensor] = None
1067
+
1068
+ def compute_expected_logits() -> Optional[torch.Tensor]:
1069
+ nonlocal expected_logits_cache
1070
+ if expected_logits_cache is not None:
1071
+ return expected_logits_cache
1072
+ if stacked_exit_pdf is None or not hidden_states_list:
1073
+ return None
1074
+ token_exit_pdf = _select_token_positions(stacked_exit_pdf)
1075
+ expected_logits = None
1076
+ for step_idx, hidden in enumerate(hidden_states_list):
1077
+ step_hidden = _select_token_positions(hidden)
1078
+ step_logits = self.lm_head(step_hidden)
1079
+ weight = (
1080
+ token_exit_pdf[..., step_idx].unsqueeze(-1).to(step_logits.dtype)
1081
+ )
1082
+ expected_logits = (
1083
+ step_logits * weight
1084
+ if expected_logits is None
1085
+ else expected_logits + step_logits * weight
1086
+ )
1087
+ expected_logits_cache = expected_logits
1088
+ return expected_logits_cache
1089
+
1090
+ logits: Optional[torch.Tensor] = None
1091
+ loss: Optional[torch.Tensor] = None
1092
+
1093
+ hidden_states = outputs.last_hidden_state
1094
+ logits = self.lm_head(hidden_states)
1095
+ logits = logits.float()
1096
+
1097
+ if labels is not None:
1098
+ shift_logits = logits[..., :-1, :].contiguous()
1099
+ shift_labels = labels[..., 1:].contiguous()
1100
+ loss_fct = nn.CrossEntropyLoss()
1101
+ shift_logits = shift_logits.view(-1, self.config.vocab_size)
1102
+ shift_labels = shift_labels.view(-1)
1103
+ shift_labels = shift_labels.to(shift_logits.device)
1104
+ loss = loss_fct(shift_logits, shift_labels)
1105
+
1106
+ result = CausalLMOutputWithPast(
1107
+ loss=loss,
1108
+ logits=logits,
1109
+ past_key_values=outputs.past_key_values,
1110
+ hidden_states=outputs.hidden_states,
1111
+ attentions=outputs.attentions,
1112
+ )
1113
+
1114
+ return result
tokenization_iquestcoder.py ADDED
@@ -0,0 +1,553 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # IQuest NVFP4 Quantized Model - Official Architecture
2
+ """Tokenization classes for IQuestCoder."""
3
+
4
+ import os
5
+ from shutil import copyfile
6
+ from typing import Any, Dict, List, Optional, Tuple, Union
7
+
8
+ import sentencepiece as spm
9
+
10
+ from transformers.tokenization_utils import AddedToken, PreTrainedTokenizer
11
+ from transformers.utils import logging
12
+
13
+
14
+ logger = logging.get_logger(__name__)
15
+
16
+ VOCAB_FILES_NAMES = {"vocab_file": "tokenizer.model"}
17
+
18
+ PRETRAINED_VOCAB_FILES_MAP = {
19
+ "vocab_file": {},
20
+ "tokenizer_file": {},
21
+ }
22
+ PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES = {}
23
+
24
+
25
+
26
+ class IQuestCoderTokenizer(PreTrainedTokenizer):
27
+
28
+ vocab_files_names = VOCAB_FILES_NAMES
29
+ pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP
30
+ max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES
31
+ model_input_names = ["input_ids", "attention_mask"]
32
+
33
+ def __init__(
34
+ self,
35
+ vocab_file,
36
+ unk_token="<unk>",
37
+ bos_token="<s>",
38
+ eos_token="</s>",
39
+ pad_token=None,
40
+ sp_model_kwargs: Optional[Dict[str, Any]] = None,
41
+ add_bos_token=True,
42
+ add_eos_token=False,
43
+ clean_up_tokenization_spaces=False,
44
+ add_prefix_space=False,
45
+ legacy=None,
46
+ use_default_system_prompt=False,
47
+ chat_template=None,
48
+ **kwargs,
49
+ ):
50
+ self.sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs
51
+ bos_token = AddedToken(bos_token, lstrip=False, rstrip=False) if isinstance(bos_token, str) else bos_token
52
+ eos_token = AddedToken(eos_token, lstrip=False, rstrip=False) if isinstance(eos_token, str) else eos_token
53
+ unk_token = AddedToken(unk_token, lstrip=False, rstrip=False) if isinstance(unk_token, str) else unk_token
54
+ pad_token = AddedToken(pad_token, lstrip=False, rstrip=False) if isinstance(pad_token, str) else pad_token
55
+
56
+ # Legacy behavior handling
57
+ if legacy is None:
58
+ logger.warning_once(
59
+ f"You are using the default legacy behaviour of the {self.__class__.__name__}. This is"
60
+ " expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you."
61
+ " If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it"
62
+ " means, and thoroughly read the reason why this was added as explained in"
63
+ " https://github.com/huggingface/transformers/pull/24565"
64
+ )
65
+ legacy = True
66
+
67
+ self.legacy = legacy
68
+ self.vocab_file = vocab_file
69
+ self.add_bos_token = add_bos_token
70
+ self.add_eos_token = add_eos_token
71
+ self.add_prefix_space = add_prefix_space
72
+ self.use_default_system_prompt = use_default_system_prompt
73
+ self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)
74
+ self.sp_model.Load(vocab_file)
75
+
76
+
77
+
78
+ super().__init__(
79
+ bos_token=bos_token,
80
+ eos_token=eos_token,
81
+ unk_token=unk_token,
82
+ pad_token=pad_token,
83
+ add_bos_token=add_bos_token,
84
+ add_eos_token=add_eos_token,
85
+ sp_model_kwargs=self.sp_model_kwargs,
86
+ clean_up_tokenization_spaces=clean_up_tokenization_spaces,
87
+ add_prefix_space=add_prefix_space,
88
+ legacy=legacy,
89
+ use_default_system_prompt=use_default_system_prompt,
90
+ chat_template=chat_template,
91
+ **kwargs,
92
+ )
93
+
94
+ def __getstate__(self):
95
+ state = self.__dict__.copy()
96
+ state["sp_model"] = None
97
+ return state
98
+
99
+ def __setstate__(self, d):
100
+ self.__dict__ = d
101
+ self.sp_model = spm.SentencePieceProcessor(**self.sp_model_kwargs)
102
+ self.sp_model.Load(self.vocab_file)
103
+
104
+ @property
105
+ def vocab_size(self) -> int:
106
+ """Returns the vocabulary size."""
107
+ return self.sp_model.get_piece_size()
108
+
109
+ def get_vocab(self) -> Dict[str, int]:
110
+ """Returns the vocabulary as a dictionary of token to index."""
111
+ vocab = {self.convert_ids_to_tokens(i): i for i in range(self.vocab_size)}
112
+ vocab.update(self.added_tokens_encoder)
113
+ return vocab
114
+
115
+ def _tokenize(self, text: str) -> List[str]:
116
+ """
117
+ Tokenize a string.
118
+
119
+ Args:
120
+ text (`str`): The text to tokenize.
121
+
122
+ Returns:
123
+ `List[str]`: The list of tokens.
124
+ """
125
+ if self.add_prefix_space:
126
+ text = " " + text
127
+
128
+ if self.legacy:
129
+ return self.sp_model.encode(text, out_type=str)
130
+
131
+ # Non-legacy behavior: handle special tokens properly
132
+ return self.sp_model.encode(text, out_type=str)
133
+
134
+ def _convert_token_to_id(self, token: str) -> int:
135
+ """Converts a token (str) to an id using the vocab."""
136
+ return self.sp_model.piece_to_id(token)
137
+
138
+ def _convert_id_to_token(self, index: int) -> str:
139
+ """Converts an index (integer) to a token (str) using the vocab."""
140
+ token = self.sp_model.IdToPiece(index)
141
+ return token
142
+
143
+ def convert_tokens_to_string(self, tokens: List[str]) -> str:
144
+ """
145
+ Converts a sequence of tokens (strings) to a single string.
146
+
147
+ This method handles special tokens separately to ensure they are not
148
+ decoded using the SentencePiece model.
149
+
150
+ Args:
151
+ tokens (`List[str]`): The list of tokens to convert.
152
+
153
+ Returns:
154
+ `str`: The decoded string.
155
+ """
156
+ current_sub_tokens = []
157
+ out_string = ""
158
+ prev_is_special = False
159
+ for i, token in enumerate(tokens):
160
+ # make sure that special tokens are not decoded using sentencepiece model
161
+ if token in self.all_special_tokens:
162
+ if not prev_is_special and i != 0:
163
+ out_string += " "
164
+ out_string += self.sp_model.decode(current_sub_tokens) + token
165
+ prev_is_special = True
166
+ current_sub_tokens = []
167
+ else:
168
+ current_sub_tokens.append(token)
169
+ prev_is_special = False
170
+ out_string += self.sp_model.decode(current_sub_tokens)
171
+ return out_string
172
+
173
+ def save_vocabulary(self, save_directory: str, filename_prefix: Optional[str] = None) -> Tuple[str]:
174
+ """
175
+ Save the vocabulary and special tokens file to a directory.
176
+
177
+ Args:
178
+ save_directory (`str`):
179
+ The directory in which to save the vocabulary.
180
+ filename_prefix (`str`, *optional*):
181
+ An optional prefix to add to the named of the saved files.
182
+
183
+ Returns:
184
+ `Tuple(str)`: Paths to the files saved.
185
+ """
186
+ if not os.path.isdir(save_directory):
187
+ logger.error(f"Vocabulary path ({save_directory}) should be a directory")
188
+ return
189
+ out_vocab_file = os.path.join(
190
+ save_directory, (filename_prefix + "-" if filename_prefix else "") + VOCAB_FILES_NAMES["vocab_file"]
191
+ )
192
+
193
+ if os.path.abspath(self.vocab_file) != os.path.abspath(out_vocab_file) and os.path.isfile(self.vocab_file):
194
+ copyfile(self.vocab_file, out_vocab_file)
195
+ elif not os.path.isfile(self.vocab_file):
196
+ with open(out_vocab_file, "wb") as fi:
197
+ content_spiece_model = self.sp_model.serialized_model_proto()
198
+ fi.write(content_spiece_model)
199
+
200
+ return (out_vocab_file,)
201
+
202
+ def build_inputs_with_special_tokens(
203
+ self,
204
+ token_ids_0: List[int],
205
+ token_ids_1: Optional[List[int]] = None
206
+ ) -> List[int]:
207
+ """
208
+ Build model inputs from a sequence or a pair of sequences for sequence classification tasks by concatenating
209
+ and adding special tokens.
210
+
211
+ An IQuestCoder sequence has the following format:
212
+
213
+ - single sequence: `<s> X </s>` (if add_eos_token is True) or `<s> X` (default)
214
+ - pair of sequences: `<s> A </s> <s> B </s>` (if add_eos_token is True) or `<s> A <s> B` (default)
215
+
216
+ Args:
217
+ token_ids_0 (`List[int]`):
218
+ List of IDs to which the special tokens will be added.
219
+ token_ids_1 (`List[int]`, *optional*):
220
+ Optional second list of IDs for sequence pairs.
221
+
222
+ Returns:
223
+ `List[int]`: List of input IDs with the appropriate special tokens.
224
+ """
225
+ bos_token_id = [self.bos_token_id] if self.add_bos_token else []
226
+ eos_token_id = [self.eos_token_id] if self.add_eos_token else []
227
+
228
+ output = bos_token_id + token_ids_0 + eos_token_id
229
+
230
+ if token_ids_1 is not None:
231
+ output = output + bos_token_id + token_ids_1 + eos_token_id
232
+
233
+ return output
234
+
235
+ def get_special_tokens_mask(
236
+ self,
237
+ token_ids_0: List[int],
238
+ token_ids_1: Optional[List[int]] = None,
239
+ already_has_special_tokens: bool = False
240
+ ) -> List[int]:
241
+ """
242
+ Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding
243
+ special tokens using the tokenizer `prepare_for_model` method.
244
+
245
+ Args:
246
+ token_ids_0 (`List[int]`):
247
+ List of IDs.
248
+ token_ids_1 (`List[int]`, *optional*):
249
+ Optional second list of IDs for sequence pairs.
250
+ already_has_special_tokens (`bool`, *optional*, defaults to `False`):
251
+ Whether or not the token list is already formatted with special tokens for the model.
252
+
253
+ Returns:
254
+ `List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.
255
+ """
256
+ if already_has_special_tokens:
257
+ return super().get_special_tokens_mask(
258
+ token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True
259
+ )
260
+
261
+ bos_token_id = [1] if self.add_bos_token else []
262
+ eos_token_id = [1] if self.add_eos_token else []
263
+
264
+ if token_ids_1 is None:
265
+ return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id
266
+ return (
267
+ bos_token_id
268
+ + ([0] * len(token_ids_0))
269
+ + eos_token_id
270
+ + bos_token_id
271
+ + ([0] * len(token_ids_1))
272
+ + eos_token_id
273
+ )
274
+
275
+ def create_token_type_ids_from_sequences(
276
+ self,
277
+ token_ids_0: List[int],
278
+ token_ids_1: Optional[List[int]] = None
279
+ ) -> List[int]:
280
+ """
281
+ Create a mask from the two sequences passed to be used in a sequence-pair classification task.
282
+
283
+ An IQuestCoder sequence pair mask has the following format:
284
+
285
+ ```
286
+ 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1
287
+ | first sequence | second sequence |
288
+ ```
289
+
290
+ If `token_ids_1` is `None`, this method only returns the first portion of the mask (0s).
291
+
292
+ Args:
293
+ token_ids_0 (`List[int]`):
294
+ List of IDs.
295
+ token_ids_1 (`List[int]`, *optional*):
296
+ Optional second list of IDs for sequence pairs.
297
+
298
+ Returns:
299
+ `List[int]`: List of token type IDs according to the given sequence(s).
300
+ """
301
+ bos_token_id = [self.bos_token_id] if self.add_bos_token else []
302
+ eos_token_id = [self.eos_token_id] if self.add_eos_token else []
303
+
304
+ output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)
305
+
306
+ if token_ids_1 is not None:
307
+ output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)
308
+
309
+ return output
310
+
311
+ @property
312
+ def default_chat_template(self) -> str:
313
+ """
314
+ Returns the default chat template for IQuestCoder.
315
+
316
+ This template formats conversations with system, user, and assistant roles.
317
+ """
318
+ return DEFAULT_CHAT_TEMPLATE
319
+
320
+ def apply_chat_template(
321
+ self,
322
+ conversation: Union[List[Dict[str, str]], "Conversation"],
323
+ chat_template: Optional[str] = None,
324
+ add_generation_prompt: bool = False,
325
+ tokenize: bool = True,
326
+ padding: bool = False,
327
+ truncation: bool = False,
328
+ max_length: Optional[int] = None,
329
+ return_tensors: Optional[str] = None,
330
+ return_dict: bool = False,
331
+ **tokenizer_kwargs,
332
+ ):
333
+ """
334
+ Apply a chat template to format a conversation.
335
+
336
+ Args:
337
+ conversation (`List[Dict[str, str]]` or `Conversation`):
338
+ A list of dicts with "role" and "content" keys, representing the conversation history.
339
+ chat_template (`str`, *optional*):
340
+ A Jinja template to use for formatting. If not provided, the tokenizer's default will be used.
341
+ add_generation_prompt (`bool`, *optional*, defaults to `False`):
342
+ Whether to add a generation prompt at the end for the assistant to continue.
343
+ tokenize (`bool`, *optional*, defaults to `True`):
344
+ Whether to tokenize the output. If `False`, returns a string.
345
+ padding (`bool`, *optional*, defaults to `False`):
346
+ Whether to pad sequences.
347
+ truncation (`bool`, *optional*, defaults to `False`):
348
+ Whether to truncate sequences.
349
+ max_length (`int`, *optional*):
350
+ Maximum length of the output.
351
+ return_tensors (`str`, *optional*):
352
+ The type of tensors to return ("pt", "tf", "np", or None).
353
+ return_dict (`bool`, *optional*, defaults to `False`):
354
+ Whether to return a dictionary with additional information.
355
+ **tokenizer_kwargs:
356
+ Additional keyword arguments passed to the tokenizer.
357
+
358
+ Returns:
359
+ `Union[str, List[int], BatchEncoding]`: The formatted (and optionally tokenized) conversation.
360
+
361
+ Example:
362
+ ```python
363
+ >>> tokenizer = IQuestCoderTokenizer.from_pretrained("path/to/model")
364
+ >>> conversation = [
365
+ ... {"role": "system", "content": "You are a helpful assistant."},
366
+ ... {"role": "user", "content": "Hello!"},
367
+ ... {"role": "assistant", "content": "Hi there! How can I help you today?"},
368
+ ... {"role": "user", "content": "What's the weather like?"},
369
+ ... ]
370
+ >>> tokenizer.apply_chat_template(conversation, add_generation_prompt=True, tokenize=False)
371
+ '<|system|>\\nYou are a helpful assistant.\\n</|system|><|user|>\\nHello!\\n</|user|>...'
372
+ ```
373
+ """
374
+ # Use parent class implementation with our template
375
+ return super().apply_chat_template(
376
+ conversation,
377
+ chat_template=chat_template,
378
+ add_generation_prompt=add_generation_prompt,
379
+ tokenize=tokenize,
380
+ padding=padding,
381
+ truncation=truncation,
382
+ max_length=max_length,
383
+ return_tensors=return_tensors,
384
+ return_dict=return_dict,
385
+ **tokenizer_kwargs,
386
+ )
387
+
388
+
389
+ # Try to import and create Fast tokenizer version
390
+ try:
391
+ from transformers import PreTrainedTokenizerFast
392
+ from tokenizers import Tokenizer, decoders, models, normalizers, pre_tokenizers, processors
393
+
394
+ class IQuestCoderTokenizerFast(PreTrainedTokenizerFast):
395
+ """
396
+ Construct a "fast" IQuestCoder tokenizer (backed by HuggingFace's *tokenizers* library).
397
+
398
+ This is a fast implementation of [`IQuestCoderTokenizer`] using the 🤗 Tokenizers library.
399
+
400
+ Args:
401
+ vocab_file (`str`, *optional*):
402
+ Path to the vocabulary file (SentencePiece model).
403
+ tokenizer_file (`str`, *optional*):
404
+ Path to a tokenizer JSON file.
405
+ unk_token (`str`, *optional*, defaults to `"<unk>"`):
406
+ The unknown token.
407
+ bos_token (`str`, *optional*, defaults to `"<s>"`):
408
+ The beginning of sequence token.
409
+ eos_token (`str`, *optional*, defaults to `"</s>"`):
410
+ The end of sequence token.
411
+ pad_token (`str`, *optional*):
412
+ The token used for padding.
413
+ add_bos_token (`bool`, *optional*, defaults to `True`):
414
+ Whether to add a BOS token at the start of sequences.
415
+ add_eos_token (`bool`, *optional*, defaults to `False`):
416
+ Whether to add an EOS token at the end of sequences.
417
+ add_prefix_space (`bool`, *optional*, defaults to `False`):
418
+ Whether to add an initial space to the input.
419
+ use_default_system_prompt (`bool`, *optional*, defaults to `False`):
420
+ Whether to use the default system prompt.
421
+ chat_template (`str`, *optional*):
422
+ A Jinja template for formatting conversations.
423
+
424
+ Example:
425
+ ```python
426
+ >>> from tokenization_iquestcoder import IQuestCoderTokenizerFast
427
+
428
+ >>> tokenizer = IQuestCoderTokenizerFast.from_pretrained("path/to/model")
429
+ >>> tokenizer.encode("Hello, world!")
430
+ [1, 15043, 29892, 3186, 29991]
431
+ ```
432
+ """
433
+
434
+ vocab_files_names = VOCAB_FILES_NAMES
435
+ pretrained_vocab_files_map = PRETRAINED_VOCAB_FILES_MAP
436
+ max_model_input_sizes = PRETRAINED_POSITIONAL_EMBEDDINGS_SIZES
437
+ model_input_names = ["input_ids", "attention_mask"]
438
+ slow_tokenizer_class = IQuestCoderTokenizer
439
+
440
+ def __init__(
441
+ self,
442
+ vocab_file=None,
443
+ tokenizer_file=None,
444
+ unk_token="<unk>",
445
+ bos_token="<s>",
446
+ eos_token="</s>",
447
+ pad_token=None,
448
+ add_bos_token=True,
449
+ add_eos_token=False,
450
+ add_prefix_space=False,
451
+ use_default_system_prompt=False,
452
+ chat_template=None,
453
+ **kwargs,
454
+ ):
455
+ self.add_bos_token = add_bos_token
456
+ self.add_eos_token = add_eos_token
457
+ self.add_prefix_space = add_prefix_space
458
+ self.use_default_system_prompt = use_default_system_prompt
459
+
460
+ if chat_template is None:
461
+ chat_template = DEFAULT_CHAT_TEMPLATE
462
+
463
+ super().__init__(
464
+ vocab_file=vocab_file,
465
+ tokenizer_file=tokenizer_file,
466
+ unk_token=unk_token,
467
+ bos_token=bos_token,
468
+ eos_token=eos_token,
469
+ pad_token=pad_token,
470
+ add_bos_token=add_bos_token,
471
+ add_eos_token=add_eos_token,
472
+ add_prefix_space=add_prefix_space,
473
+ use_default_system_prompt=use_default_system_prompt,
474
+ chat_template=chat_template,
475
+ **kwargs,
476
+ )
477
+
478
+ @property
479
+ def can_save_slow_tokenizer(self) -> bool:
480
+ return os.path.isfile(self.vocab_file) if self.vocab_file else False
481
+
482
+ @property
483
+ def default_chat_template(self) -> str:
484
+ """Returns the default chat template."""
485
+ return DEFAULT_CHAT_TEMPLATE
486
+
487
+ def build_inputs_with_special_tokens(
488
+ self,
489
+ token_ids_0: List[int],
490
+ token_ids_1: Optional[List[int]] = None
491
+ ) -> List[int]:
492
+ """Build model inputs with special tokens."""
493
+ bos_token_id = [self.bos_token_id] if self.add_bos_token else []
494
+ eos_token_id = [self.eos_token_id] if self.add_eos_token else []
495
+
496
+ output = bos_token_id + token_ids_0 + eos_token_id
497
+
498
+ if token_ids_1 is not None:
499
+ output = output + bos_token_id + token_ids_1 + eos_token_id
500
+
501
+ return output
502
+
503
+ def get_special_tokens_mask(
504
+ self,
505
+ token_ids_0: List[int],
506
+ token_ids_1: Optional[List[int]] = None,
507
+ already_has_special_tokens: bool = False
508
+ ) -> List[int]:
509
+ """Retrieve special tokens mask."""
510
+ if already_has_special_tokens:
511
+ return super().get_special_tokens_mask(
512
+ token_ids_0=token_ids_0, token_ids_1=token_ids_1, already_has_special_tokens=True
513
+ )
514
+
515
+ bos_token_id = [1] if self.add_bos_token else []
516
+ eos_token_id = [1] if self.add_eos_token else []
517
+
518
+ if token_ids_1 is None:
519
+ return bos_token_id + ([0] * len(token_ids_0)) + eos_token_id
520
+ return (
521
+ bos_token_id
522
+ + ([0] * len(token_ids_0))
523
+ + eos_token_id
524
+ + bos_token_id
525
+ + ([0] * len(token_ids_1))
526
+ + eos_token_id
527
+ )
528
+
529
+ def create_token_type_ids_from_sequences(
530
+ self,
531
+ token_ids_0: List[int],
532
+ token_ids_1: Optional[List[int]] = None
533
+ ) -> List[int]:
534
+ """Create token type IDs from sequences."""
535
+ bos_token_id = [self.bos_token_id] if self.add_bos_token else []
536
+ eos_token_id = [self.eos_token_id] if self.add_eos_token else []
537
+
538
+ output = [0] * len(bos_token_id + token_ids_0 + eos_token_id)
539
+
540
+ if token_ids_1 is not None:
541
+ output += [1] * len(bos_token_id + token_ids_1 + eos_token_id)
542
+
543
+ return output
544
+
545
+ except ImportError:
546
+ # tokenizers library not available, Fast tokenizer not supported
547
+ IQuestCoderTokenizerFast = None
548
+ logger.info(
549
+ "The `tokenizers` library is not installed. "
550
+ "IQuestCoderTokenizerFast will not be available. "
551
+ "Install it with `pip install tokenizers`."
552
+ )
553
+
tokenizer.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7d3be68e090a927f31e0e378d7599b15c206dd47e4a73933775a746cc9c1cd91
3
+ size 1345108
tokenizer_config.json ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_eos_token": false,
4
+ "added_tokens_decoder": {
5
+ "0": {
6
+ "content": "<unk>",
7
+ "lstrip": false,
8
+ "normalized": true,
9
+ "rstrip": false,
10
+ "single_word": true,
11
+ "special": true
12
+ },
13
+ "1": {
14
+ "content": "<s>",
15
+ "lstrip": false,
16
+ "normalized": true,
17
+ "rstrip": false,
18
+ "single_word": false,
19
+ "special": true
20
+ },
21
+ "2": {
22
+ "content": "</s>",
23
+ "lstrip": false,
24
+ "normalized": true,
25
+ "rstrip": false,
26
+ "single_word": true,
27
+ "special": true
28
+ },
29
+ "75858": {
30
+ "content": "<CLS>",
31
+ "lstrip": false,
32
+ "normalized": false,
33
+ "rstrip": false,
34
+ "single_word": false,
35
+ "special": true
36
+ },
37
+ "75859": {
38
+ "content": "<SEP>",
39
+ "lstrip": false,
40
+ "normalized": false,
41
+ "rstrip": false,
42
+ "single_word": false,
43
+ "special": true
44
+ },
45
+ "75860": {
46
+ "content": "<EOD>",
47
+ "lstrip": false,
48
+ "normalized": false,
49
+ "rstrip": false,
50
+ "single_word": false,
51
+ "special": true
52
+ },
53
+ "75861": {
54
+ "content": "<MASK>",
55
+ "lstrip": false,
56
+ "normalized": false,
57
+ "rstrip": false,
58
+ "single_word": false,
59
+ "special": true
60
+ },
61
+ "75862": {
62
+ "content": "<PAD>",
63
+ "lstrip": false,
64
+ "normalized": false,
65
+ "rstrip": false,
66
+ "single_word": false,
67
+ "special": true
68
+ },
69
+ "75863": {
70
+ "content": "<|im_start|>",
71
+ "lstrip": false,
72
+ "normalized": false,
73
+ "rstrip": false,
74
+ "single_word": false,
75
+ "special": true
76
+ },
77
+ "75864": {
78
+ "content": "<|im_end|>",
79
+ "lstrip": false,
80
+ "normalized": false,
81
+ "rstrip": false,
82
+ "single_word": false,
83
+ "special": true
84
+ },
85
+ "75865": {
86
+ "content": "<|fim_prefix|>",
87
+ "lstrip": false,
88
+ "normalized": false,
89
+ "rstrip": false,
90
+ "single_word": false,
91
+ "special": true
92
+ },
93
+ "75866": {
94
+ "content": "<|fim_middle|>",
95
+ "lstrip": false,
96
+ "normalized": false,
97
+ "rstrip": false,
98
+ "single_word": false,
99
+ "special": true
100
+ },
101
+ "75867": {
102
+ "content": "<|fim_suffix|>",
103
+ "lstrip": false,
104
+ "normalized": false,
105
+ "rstrip": false,
106
+ "single_word": false,
107
+ "special": true
108
+ },
109
+ "75868": {
110
+ "content": "<|fim_pad|>",
111
+ "lstrip": false,
112
+ "normalized": false,
113
+ "rstrip": false,
114
+ "single_word": false,
115
+ "special": true
116
+ },
117
+ "75869": {
118
+ "content": "<|endoftext|>",
119
+ "lstrip": false,
120
+ "normalized": false,
121
+ "rstrip": false,
122
+ "single_word": false,
123
+ "special": true
124
+ },
125
+ "75870": {
126
+ "content": "<|repo_name|>",
127
+ "lstrip": false,
128
+ "normalized": false,
129
+ "rstrip": false,
130
+ "single_word": false,
131
+ "special": true
132
+ },
133
+ "75871": {
134
+ "content": "<|file_sep|>",
135
+ "lstrip": false,
136
+ "normalized": false,
137
+ "rstrip": false,
138
+ "single_word": false,
139
+ "special": true
140
+ },
141
+ "75872": {
142
+ "content": "<think>",
143
+ "lstrip": false,
144
+ "normalized": false,
145
+ "rstrip": false,
146
+ "single_word": false,
147
+ "special": false
148
+ },
149
+ "75873": {
150
+ "content": "</think>",
151
+ "lstrip": false,
152
+ "normalized": false,
153
+ "rstrip": false,
154
+ "single_word": false,
155
+ "special": false
156
+ },
157
+ "75874": {
158
+ "content": "<tools>",
159
+ "lstrip": false,
160
+ "normalized": false,
161
+ "rstrip": false,
162
+ "single_word": false,
163
+ "special": false
164
+ },
165
+ "75875": {
166
+ "content": "</tools>",
167
+ "lstrip": false,
168
+ "normalized": false,
169
+ "rstrip": false,
170
+ "single_word": false,
171
+ "special": false
172
+ },
173
+ "75876": {
174
+ "content": "<tool_call>",
175
+ "lstrip": false,
176
+ "normalized": false,
177
+ "rstrip": false,
178
+ "single_word": false,
179
+ "special": false
180
+ },
181
+ "75877": {
182
+ "content": "</tool_call>",
183
+ "lstrip": false,
184
+ "normalized": false,
185
+ "rstrip": false,
186
+ "single_word": false,
187
+ "special": false
188
+ },
189
+ "75878": {
190
+ "content": "<tool_response>",
191
+ "lstrip": false,
192
+ "normalized": false,
193
+ "rstrip": false,
194
+ "single_word": false,
195
+ "special": false
196
+ },
197
+ "75879": {
198
+ "content": "</tool_response>",
199
+ "lstrip": false,
200
+ "normalized": false,
201
+ "rstrip": false,
202
+ "single_word": false,
203
+ "special": false
204
+ }
205
+ },
206
+ "additional_special_tokens": [
207
+ "<|CLS|>",
208
+ "<|SEP|>",
209
+ "<|EOD|>",
210
+ "<|MASK|>",
211
+ "<|PAD|>",
212
+ "<|fim_prefix|>",
213
+ "<|fim_middle|>",
214
+ "<|fim_suffix|>",
215
+ "<|im_start|>",
216
+ "<|im_end|>",
217
+ "<|fim_pad|>",
218
+ "<|endoftext|>",
219
+ "<|repo_name|>",
220
+ "<|file_sep|>",
221
+ "<think>",
222
+ "</think>"
223
+ ],
224
+ "auto_map": {
225
+ "AutoTokenizer": [
226
+ "tokenization_iquestcoder.IQuestCoderTokenizer",
227
+ null
228
+ ]
229
+ },
230
+ "bos_token": "<s>",
231
+ "chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0].role == 'system' %}\n {{- messages[0].content + '\\n\\n' }}\n {%- else %}\n {{- 'You are LoopCoder, a helpful assistant developed by IQuest.' }}\n {%- endif %}\n {{- \"# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within <tools></tools> XML tags:\\n<tools>\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call><|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0].role == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0].content + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are LoopCoder, a helpful assistant developed by IQuest.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}\n{%- for message in messages[::-1] %}\n {%- set index = (messages|length - 1) - loop.index0 %}\n {%- if ns.multi_step_tool and message.role == \"user\" and not(message.content.startswith('<tool_response>') and message.content.endswith('</tool_response>')) %}\n {%- set ns.multi_step_tool = false %}\n {%- set ns.last_query_index = index %}\n {%- endif %}\n{%- endfor %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {%- set content = message.content %}\n {{- '<|im_start|>' + message.role + '\\n' + content }}\n {%- if message.tool_calls %}\n {%- for tool_call in message.tool_calls %}\n {%- if (loop.first and content) or (not loop.first) %}\n {{- '\\n' }}\n {%- endif %}\n {%- if tool_call.function %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '<tool_call>\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {%- if tool_call.arguments is string %}\n {{- tool_call.arguments }}\n {%- else %}\n {{- tool_call.arguments | tojson }}\n {%- endif %}\n {{- '}\\n</tool_call>' }}\n {%- endfor %}\n {%- endif %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if loop.first or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n<tool_response>\\n' }}\n {{- message.content }}\n {{- '\\n</tool_response>' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}",
232
+ "clean_up_tokenization_spaces": false,
233
+ "eos_token": "<|im_end|>",
234
+ "model_max_length": 16384,
235
+ "pad_token": "<|endoftext|>",
236
+ "padding_side": "right",
237
+ "sp_model_kwargs": {},
238
+ "split_special_tokens": false,
239
+ "tokenizer_class": "IQuestCoderTokenizer",
240
+ "unk_token": "<unk>",
241
+ "use_fast": false
242
+ }