Joshuant commited on
Commit
b397d04
·
verified ·
1 Parent(s): 66cd5a8

emotion-conditioned Indian-English TTS (non-commercial demo, Skit-AI BY-NC)

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-nc-4.0
3
+ language:
4
+ - en
5
+ library_name: transformers
6
+ pipeline_tag: text-to-speech
7
+ base_model: OpenMOSS-Team/MOSS-TTS-Local-Transformer
8
+ tags:
9
+ - text-to-speech
10
+ - tts
11
+ - emotion
12
+ - expressive
13
+ - indian-english
14
+ - lora
15
+ - non-commercial
16
+ ---
17
+
18
+ # Emotion TTS, Indian-English (proof-of-concept)
19
+
20
+ A non-commercial research demo. An emotion-conditioned Indian-English voice: one consistent
21
+ female speaker with steerable emotion (neutral, happy, sad, angry, excited, calm, apologetic,
22
+ fear), selected per sentence through a text instruction. It is a LoRA fine-tune of the 1.7B
23
+ MOSS-TTS-Local-Transformer.
24
+
25
+ > NON-COMMERCIAL. This model was trained on the Skit-AI Emotional TTS dataset, which is
26
+ > licensed CC BY-NC 4.0. The model is therefore released under CC BY-NC 4.0 and must not be
27
+ > used for commercial purposes. For commercial use, obtain a license from Skit-AI.
28
+
29
+ ## Why this exists
30
+
31
+ Part of the Roxi TTS research line. The commercial Roxi voices are neutral read-speech,
32
+ because their training speaker had little emotional range, so emotion instructions had no
33
+ effect. This demo shows that with an emotionally varied speaker, the same pipeline produces
34
+ genuinely steerable emotion in a single consistent voice, controlled by instruction.
35
+
36
+ ## Emotions and how to steer
37
+
38
+ Set the `instruction` field to one of the following:
39
+
40
+ | Emotion | Instruction |
41
+ |---|---|
42
+ | neutral | Speak in a neutral, clear, conversational Indian-English style. |
43
+ | happy | Speak in a happy, cheerful, warm tone, in a clear Indian-English style. |
44
+ | sad | Speak in a sad, downcast, sorrowful tone, in a clear Indian-English style. |
45
+ | angry | Speak in an angry, irritated, forceful tone, in a clear Indian-English style. |
46
+ | excited | Speak in an excited, high-energy, enthusiastic tone, in a clear Indian-English style. |
47
+ | calm | Speak in a calm, relaxed, soothing tone, in a clear Indian-English style. |
48
+ | apologetic | Speak in an apologetic, regretful, gentle tone, in a clear Indian-English style. |
49
+ | fear | Speak in a fearful, anxious, worried tone, in a clear Indian-English style. |
50
+
51
+ ## Model at a glance
52
+
53
+ | Field | Value |
54
+ |---|---|
55
+ | Base model | OpenMOSS-Team/MOSS-TTS-Local-Transformer (1.7B, Apache-2.0) |
56
+ | Audio tokenizer | OpenMOSS-Team/MOSS-Audio-Tokenizer (Apache-2.0) |
57
+ | Method | LoRA (PEFT), r=16, alpha=32, merged into the base weights, bf16 |
58
+ | Training data | Skit-AI Emotional TTS, single Indian-English female speaker, 8 emotions, about 2825 clips |
59
+ | Output | 24 kHz mono |
60
+ | Voice consistency | 0.958 speaker-similarity across emotions (WavLM-SV) |
61
+ | Emotion range | 2.1x pitch-variation spread across the eight emotions |
62
+ | Speed | Real-time factor about 2.5 on a 16 GB GPU, not real-time |
63
+
64
+ ## Requirements
65
+
66
+ Built for transformers 4.57.1. Install the MOSS-TTS repository so the model class is importable.
67
+
68
+ ```bash
69
+ pip install "transformers==4.57.1" torch torchaudio soundfile librosa peft
70
+ git clone https://github.com/OpenMOSS/MOSS-TTS.git
71
+ ```
72
+
73
+ ## Usage
74
+
75
+ ```python
76
+ import sys, torch, soundfile as sf
77
+ sys.path.insert(0, "MOSS-TTS")
78
+ from transformers import AutoProcessor
79
+ from moss_tts_local.modeling_moss_tts import MossTTSDelayModel
80
+
81
+ repo = "IOTEverythin/roxi-tts-emotion-demo"
82
+ device = "cuda" if torch.cuda.is_available() else "cpu"
83
+ dtype = torch.bfloat16 if device == "cuda" else torch.float32
84
+
85
+ processor = AutoProcessor.from_pretrained(repo, trust_remote_code=True)
86
+ processor.audio_tokenizer = processor.audio_tokenizer.to(device)
87
+ model = MossTTSDelayModel.from_pretrained(repo, torch_dtype=dtype, attn_implementation="sdpa").to(device).eval()
88
+
89
+ text = "I just heard the news about the meeting tomorrow."
90
+ instruction = "Speak in an excited, high-energy, enthusiastic tone, in a clear Indian-English style."
91
+
92
+ conv = [[processor.build_user_message(text=text, instruction=instruction)]]
93
+ batch = processor(conv, mode="generation")
94
+ out = model.generate(
95
+ input_ids=batch["input_ids"].to(device),
96
+ attention_mask=batch["attention_mask"].to(device),
97
+ max_new_tokens=4096, do_sample=True, temperature=0.9,
98
+ )
99
+ audio = processor.decode(out)[0].audio_codes_list[0]
100
+ sf.write("out.wav", audio.float().cpu().numpy(), processor.model_config.sampling_rate)
101
+ ```
102
+
103
+ Generation is autoregressive and can under-generate. If a clip is short, generate a few
104
+ times and keep the longest, then trim silence. Keep sentences to about twelve words.
105
+
106
+ ## Limitations
107
+
108
+ - Non-commercial license (see below).
109
+ - Not real-time on a single consumer GPU.
110
+ - Eight emotions. Surprise is excluded because the source had no audio for it.
111
+ - Emotion is set per sentence through the instruction, not through inline tags inside a sentence.
112
+ - Requires transformers 4.57.1.
113
+
114
+ ## Attribution and license
115
+
116
+ This model is released under CC BY-NC 4.0 (non-commercial). It is built on
117
+ MOSS-TTS-Local-Transformer (Apache-2.0) and the MOSS Audio Tokenizer (Apache-2.0). The
118
+ emotion control is learned from the Skit-AI Emotional TTS dataset
119
+ (https://github.com/skit-ai/emotion-tts-dataset), which is licensed CC BY-NC 4.0,
120
+ copyright Skit.ai. Because the training data is non-commercial, this derivative model is
121
+ non-commercial as well.
122
+
123
+ ## Responsible use
124
+
125
+ This voice is derived from a real dataset speaker. Do not use it to impersonate real people
126
+ or for fraud, social engineering, or deception. Disclose AI-generated audio where required by
127
+ law or policy. Provided as is, without warranty.
__init__.py ADDED
File without changes
added_tokens.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "</think>": 151668,
3
+ "</tool_call>": 151658,
4
+ "</tool_response>": 151666,
5
+ "<think>": 151667,
6
+ "<tool_call>": 151657,
7
+ "<tool_response>": 151665,
8
+ "<|audio_end|>": 151653,
9
+ "<|audio_pad|>": 151654,
10
+ "<|audio_start|>": 151652,
11
+ "<|box_end|>": 151649,
12
+ "<|box_start|>": 151648,
13
+ "<|endoftext|>": 151643,
14
+ "<|file_sep|>": 151664,
15
+ "<|fim_middle|>": 151660,
16
+ "<|fim_pad|>": 151662,
17
+ "<|fim_prefix|>": 151659,
18
+ "<|fim_suffix|>": 151661,
19
+ "<|im_end|>": 151645,
20
+ "<|im_start|>": 151644,
21
+ "<|image_pad|>": 151655,
22
+ "<|object_ref_end|>": 151647,
23
+ "<|object_ref_start|>": 151646,
24
+ "<|quad_end|>": 151651,
25
+ "<|quad_start|>": 151650,
26
+ "<|repo_name|>": 151663,
27
+ "<|video_pad|>": 151656
28
+ }
chat_template.jinja ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {% for message in messages %}<|im_start|>{{ message['role'] }}
2
+ {% if message['content'] is string %}{{ message['content'] }}{% else %}{% for content in message['content'] %}{% if content.get('type') == 'text' %}{{ content['text'] }}{% endif %}{% endfor %}{% endif %}<|im_end|>
3
+ {% endfor %}{% if add_generation_prompt %}<|im_start|>assistant
4
+ {% endif %}
config.json ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_mlp_ffn_hidden_size": 2048,
3
+ "architectures": [
4
+ "MossTTSDelayModel"
5
+ ],
6
+ "audio_assistant_delay_slot_token_id": 151662,
7
+ "audio_assistant_gen_slot_token_id": 151656,
8
+ "audio_end_token_id": 151653,
9
+ "audio_pad_code": 1024,
10
+ "audio_start_token_id": 151652,
11
+ "audio_user_slot_token_id": 151654,
12
+ "audio_vocab_size": 1024,
13
+ "auto_map": {
14
+ "AutoConfig": "configuration_moss_tts.MossTTSDelayConfig",
15
+ "AutoModel": "modeling_moss_tts.MossTTSDelayModel"
16
+ },
17
+ "dtype": "bfloat16",
18
+ "hidden_size": 2048,
19
+ "im_end_token_id": 151645,
20
+ "im_start_token_id": 151644,
21
+ "initializer_range": 0.02,
22
+ "language_config": {
23
+ "_name_or_path": "Qwen/Qwen3-8B",
24
+ "architectures": [
25
+ "Qwen3ForCausalLM"
26
+ ],
27
+ "attention_bias": false,
28
+ "attention_dropout": 0.0,
29
+ "bos_token_id": 151643,
30
+ "eos_token_id": 151645,
31
+ "head_dim": 128,
32
+ "hidden_act": "silu",
33
+ "hidden_size": 2048,
34
+ "initializer_range": 0.02,
35
+ "intermediate_size": 6144,
36
+ "layer_types": [
37
+ "full_attention",
38
+ "full_attention",
39
+ "full_attention",
40
+ "full_attention",
41
+ "full_attention",
42
+ "full_attention",
43
+ "full_attention",
44
+ "full_attention",
45
+ "full_attention",
46
+ "full_attention",
47
+ "full_attention",
48
+ "full_attention",
49
+ "full_attention",
50
+ "full_attention",
51
+ "full_attention",
52
+ "full_attention",
53
+ "full_attention",
54
+ "full_attention",
55
+ "full_attention",
56
+ "full_attention",
57
+ "full_attention",
58
+ "full_attention",
59
+ "full_attention",
60
+ "full_attention",
61
+ "full_attention",
62
+ "full_attention",
63
+ "full_attention",
64
+ "full_attention"
65
+ ],
66
+ "max_position_embeddings": 40960,
67
+ "max_window_layers": 28,
68
+ "model_type": "qwen3",
69
+ "num_attention_heads": 16,
70
+ "num_hidden_layers": 28,
71
+ "num_key_value_heads": 8,
72
+ "pad_token_id": 151643,
73
+ "rms_norm_eps": 1e-06,
74
+ "rope_parameters": {
75
+ "rope_theta": 1000000,
76
+ "rope_type": "default"
77
+ },
78
+ "sliding_window": null,
79
+ "tie_word_embeddings": false,
80
+ "use_cache": true,
81
+ "use_sliding_window": false,
82
+ "vocab_size": 155648
83
+ },
84
+ "local_ffn_hidden_size": 8960,
85
+ "local_hidden_size": 1536,
86
+ "local_num_layers": 4,
87
+ "model_type": "moss_tts_delay",
88
+ "n_vq": 32,
89
+ "pad_token_id": 151643,
90
+ "sampling_rate": 24000,
91
+ "transformers_version": "5.0.0",
92
+ "vocab_size": 155648
93
+ }
configuration_moss_tts.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2026 OpenMOSS and the HuggingFace Inc. team. All rights reserved.
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
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """ MossTTSDelay model configuration """
16
+
17
+ from typing import Optional, Union
18
+ from transformers.configuration_utils import PretrainedConfig
19
+ from transformers.utils import logging
20
+ from transformers.models.qwen3 import Qwen3Config
21
+
22
+ logger = logging.get_logger(__name__)
23
+
24
+
25
+ class MossTTSDelayConfig(PretrainedConfig):
26
+ r"""
27
+ This is the configuration class to store the configuration of a [`MossTTSDelayModel`]. It is used to instantiate an
28
+ MossTTSDelay model according to the specified arguments, defining the model architecture. Instantiating a configuration
29
+ with the defaults will yield a similar configuration to that of the MossTTSDelay [MossTTSDelay-8B](https://huggingface.co/OpenMOSS/mosstts-8b) architecture.
30
+
31
+ Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
32
+ documentation from [`PretrainedConfig`] for more information.
33
+
34
+ Args:
35
+ language_config (`Union[Qwen3Config, dict]`, *optional*):
36
+ Configuration for the backbone language model (Qwen3).
37
+ initializer_range (`float`, *optional*, defaults to 0.02):
38
+ The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
39
+ n_vq (`int`, *optional*, defaults to 32):
40
+ Number of additional VQ (Vector Quantization) heads/channels for audio.
41
+ Determines the number of codebooks used in the audio representation.
42
+ audio_vocab_size (`int`, *optional*, defaults to 1024):
43
+ Vocabulary size for the audio tokens (codebooks 1 to N).
44
+ audio_user_slot_token_id (`int`, *optional*, defaults to 151654):
45
+ The specific token ID used as a placeholder/slot for user-side audio inputs in the prompt.
46
+ audio_assistant_gen_slot_token_id (`int`, *optional*, defaults to 151656):
47
+ The specific token ID representing the generation slot for the assistant's audio output.
48
+ Acting as the trigger for the TTS generation process.
49
+ audio_assistant_delay_slot_token_id (`int`, *optional*, defaults to 151662):
50
+ The token ID used in the 'Delay Pattern' paradigm to represent the delayed/offset positions
51
+ between different VQ channels.
52
+ audio_start_token_id (`int`, *optional*, defaults to 151652):
53
+ Special token ID used to denote the start of an audio sequence in the stream.
54
+ audio_end_token_id (`int`, *optional*, defaults to 151653):
55
+ Special token ID used to denote the end of an audio sequence (EOS for audio).
56
+ audio_pad_code (`int`, *optional*, defaults to 1024):
57
+ The padding value used within the audio VQ codebooks. Typically equals `audio_vocab_size`.
58
+ """
59
+ model_type = "moss_tts_delay"
60
+ keys_to_ignore_at_inference = ["past_key_values"]
61
+
62
+ def __init__(
63
+ self,
64
+ language_config: Optional[Union[Qwen3Config, dict]] = None,
65
+ initializer_range: float = 0.02,
66
+ n_vq: int = 32,
67
+ pad_token_id: int = 151643,
68
+ im_start_token_id: int = 151644,
69
+ im_end_token_id: int = 151645,
70
+ audio_vocab_size: int = 1024,
71
+ audio_user_slot_token_id: int = 151654,
72
+ audio_assistant_gen_slot_token_id: int = 151656,
73
+ audio_assistant_delay_slot_token_id: int = 151662,
74
+ audio_start_token_id: int = 151652,
75
+ audio_end_token_id: int = 151653,
76
+ audio_pad_code: int = 1024,
77
+ sampling_rate: int = 24000,
78
+ additional_mlp_ffn_hidden_size: int = 2048,
79
+ local_ffn_hidden_size: int = 8960,
80
+ local_hidden_size: int = 1536,
81
+ local_num_layers: int = 4,
82
+ **kwargs,
83
+ ):
84
+ if isinstance(language_config, dict):
85
+ self.language_config = Qwen3Config(**language_config)
86
+ elif language_config is None:
87
+ self.language_config = Qwen3Config()
88
+ else:
89
+ self.language_config = language_config
90
+
91
+ self.initializer_range = initializer_range
92
+ self.n_vq = n_vq
93
+ self.audio_vocab_size = audio_vocab_size
94
+ self.audio_user_slot_token_id = audio_user_slot_token_id
95
+ self.audio_assistant_gen_slot_token_id = audio_assistant_gen_slot_token_id
96
+ self.audio_assistant_delay_slot_token_id = audio_assistant_delay_slot_token_id
97
+ self.audio_start_token_id = audio_start_token_id
98
+ self.audio_end_token_id = audio_end_token_id
99
+ self.audio_pad_code = audio_pad_code
100
+ self.sampling_rate = sampling_rate
101
+
102
+ self.hidden_size = self.language_config.hidden_size
103
+ self.vocab_size = self.language_config.vocab_size
104
+ self.im_start_token_id = self.language_config
105
+ self.pad_token_id = pad_token_id
106
+ self.im_start_token_id = im_start_token_id
107
+ self.im_end_token_id = im_end_token_id
108
+
109
+ self.additional_mlp_ffn_hidden_size = additional_mlp_ffn_hidden_size
110
+ self.local_ffn_hidden_size = local_ffn_hidden_size
111
+ self.local_hidden_size = local_hidden_size
112
+ self.local_num_layers = local_num_layers
113
+
114
+ super().__init__(**kwargs)
115
+
116
+ def to_dict(self):
117
+ output = super().to_dict()
118
+ if hasattr(self.language_config, "to_dict"):
119
+ output["language_config"] = self.language_config.to_dict()
120
+ else:
121
+ output["language_config"] = self.language_config
122
+ return output
generation_config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 151643,
4
+ "eos_token_id": 151645,
5
+ "transformers_version": "5.0.0"
6
+ }
inference_utils.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torchaudio
3
+ import torch.nn.functional as F
4
+ from typing import Optional, List, Tuple
5
+ from tqdm import tqdm
6
+
7
+
8
+ def apply_top_k(logits, top_k):
9
+ batch_size, vocab_size = logits.shape
10
+ top_k = min(top_k, vocab_size)
11
+ top_k_values, top_k_indices = torch.topk(logits, top_k, dim=-1)
12
+ filtered_logits = torch.full_like(logits, float("-inf"))
13
+ batch_indices = torch.arange(batch_size).unsqueeze(-1)
14
+ filtered_logits[batch_indices, top_k_indices] = top_k_values
15
+ return filtered_logits
16
+
17
+
18
+ def apply_top_p(logits, top_p):
19
+ probs = F.softmax(logits, dim=-1)
20
+ sorted_probs, sorted_indices = torch.sort(probs, descending=True, dim=-1)
21
+ cumulative_probs = torch.cumsum(sorted_probs, dim=-1)
22
+ sorted_indices_to_remove = cumulative_probs > top_p
23
+ sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
24
+ sorted_indices_to_remove[..., 0] = False
25
+ batch_size = logits.shape[0]
26
+ filtered_logits = logits.clone()
27
+ for i in range(batch_size):
28
+ indices_to_remove = sorted_indices[i][sorted_indices_to_remove[i]]
29
+ filtered_logits[i, indices_to_remove] = float("-inf")
30
+ return filtered_logits
31
+
32
+
33
+ def apply_top_p_optimized(logits, top_p):
34
+ probs = F.softmax(logits, dim=-1)
35
+ sorted_probs, sorted_indices = torch.sort(probs, descending=True, dim=-1)
36
+
37
+ cumulative_probs = torch.cumsum(sorted_probs, dim=-1)
38
+
39
+ sorted_indices_to_remove = cumulative_probs > top_p
40
+ sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
41
+ sorted_indices_to_remove[..., 0] = False
42
+
43
+ indices_to_remove = torch.zeros_like(logits, dtype=torch.bool).scatter_(
44
+ dim=-1, index=sorted_indices, src=sorted_indices_to_remove
45
+ )
46
+
47
+ logits[indices_to_remove] = float("-inf")
48
+ return logits
49
+
50
+
51
+ def apply_repetition_penalty_delay_pattern(
52
+ logits: torch.Tensor,
53
+ prev_tokens: torch.LongTensor,
54
+ penalty: float,
55
+ ):
56
+ """
57
+ logits: [B, H, V] or [N, V]
58
+ prev_tokens: [B, T, H] or [N, T] or [B, H]
59
+
60
+ Apply the repetition penalty independently for each H (VQ head).
61
+ """
62
+ if penalty == 1.0 or prev_tokens is None:
63
+ return logits
64
+
65
+ vocab_size = logits.size(-1)
66
+
67
+ # Case 1: regular [N, V] (text layer)
68
+ if logits.dim() == 2:
69
+ prev_tokens_flat = prev_tokens.reshape(-1)
70
+ unique_tokens = torch.unique(prev_tokens_flat)
71
+
72
+ token_logits = logits[:, unique_tokens]
73
+ pos_mask = token_logits > 0
74
+ token_logits[pos_mask] /= penalty
75
+ token_logits[~pos_mask] *= penalty
76
+ logits[:, unique_tokens] = token_logits
77
+ return logits
78
+
79
+ # Case 2: Delay Pattern audio [B, H, V]
80
+ assert logits.dim() == 3, "Delay Pattern audio logits must be [B, H, V]"
81
+ B, H, V = logits.shape
82
+
83
+ for h in range(H):
84
+ # prev_tokens_h: [B, T] or [B]
85
+ prev_tokens_h = prev_tokens[..., h].reshape(-1)
86
+ unique_tokens = torch.unique(prev_tokens_h)
87
+
88
+ if unique_tokens.numel() == 0:
89
+ continue
90
+
91
+ token_logits = logits[:, h, unique_tokens]
92
+ pos_mask = token_logits > 0
93
+ token_logits[pos_mask] /= penalty
94
+ token_logits[~pos_mask] *= penalty
95
+ logits[:, h, unique_tokens] = token_logits
96
+
97
+ return logits
98
+
99
+
100
+ def sample_token(
101
+ logits,
102
+ prev_tokens: Optional[torch.LongTensor] = None,
103
+ repetition_penalty: float = 1.0,
104
+ top_p=None,
105
+ top_k=None,
106
+ do_sample=True,
107
+ ):
108
+ vocab_size = logits.size(-1)
109
+
110
+ # ===== Repetition Penalty (before reshaping!) =====
111
+ if prev_tokens is not None and repetition_penalty != 1.0:
112
+ logits = apply_repetition_penalty_delay_pattern(
113
+ logits,
114
+ prev_tokens,
115
+ repetition_penalty,
116
+ )
117
+
118
+ if not do_sample:
119
+ return torch.argmax(logits, dim=-1)
120
+
121
+ # ===== Only flatten after this, for top-k / top-p / multinomial =====
122
+ original_shape = logits.shape
123
+ reshaped_logits = logits.view(-1, vocab_size)
124
+
125
+ if top_k is not None and top_k > 0:
126
+ reshaped_logits = apply_top_k(reshaped_logits, top_k)
127
+
128
+ if top_p is not None and top_p < 1.0:
129
+ reshaped_logits = apply_top_p_optimized(reshaped_logits, top_p)
130
+
131
+ probs = F.softmax(reshaped_logits, dim=-1)
132
+ next_tokens = torch.multinomial(probs, num_samples=1)
133
+
134
+ return next_tokens.view(original_shape[:-1])
135
+
136
+
137
+ def find_last_equal_C(tensor, C):
138
+ """
139
+ tensor: torch.Tensor of shape [batch_size, seq_len]
140
+ C: scalar value to match
141
+ Returns: torch.Tensor of shape [batch_size] with last indices
142
+ """
143
+ mask = (tensor == C).int() # Shape: [batch_size, seq_len], bool tensor
144
+ flipped_mask = mask.flip(dims=[1]) # Flip along sequence dimension
145
+ flipped_indices = flipped_mask.argmax(dim=1) # First True in flipped
146
+ seq_len = tensor.shape[1]
147
+ last_indices = (seq_len - 1) - flipped_indices # Convert to original indices
148
+
149
+ # Optional: Handle cases with no C (set to -1), though problem assumes existence
150
+ actual_values = tensor[torch.arange(tensor.shape[0]), last_indices]
151
+ no_match = actual_values != C
152
+ last_indices[no_match] = -1
153
+
154
+ return last_indices
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:854abd9d93c70176b7cac78f5bf14baadb85194b45df592e43ed6c81dcb317af
3
+ size 6121281992
modeling_moss_tts.py ADDED
@@ -0,0 +1,880 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import copy
3
+ import torch
4
+ import torch.nn as nn
5
+ import logging
6
+ import sys
7
+
8
+ from tqdm import tqdm
9
+ from dataclasses import dataclass
10
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
11
+ from transformers.utils import ModelOutput
12
+ from transformers.cache_utils import Cache
13
+ from typing import Optional, List, Tuple, Union
14
+ from transformers.loss.loss_utils import ForCausalLMLoss
15
+ from transformers import PreTrainedModel, GenerationMixin
16
+ from transformers.generation.streamers import BaseStreamer
17
+ from transformers.models.qwen3.modeling_qwen3 import Qwen3Model, Qwen3Attention, eager_attention_forward
18
+ from transformers.modeling_outputs import BaseModelOutputWithPast
19
+ from transformers.models.qwen3.configuration_qwen3 import Qwen3Config
20
+ from transformers.generation.configuration_utils import GenerationConfig
21
+ from transformers.generation.stopping_criteria import StoppingCriteriaList
22
+ from transformers.generation.logits_process import LogitsProcessorList, RepetitionPenaltyLogitsProcessor, TopKLogitsWarper, TopPLogitsWarper, TemperatureLogitsWarper
23
+ from transformers.masking_utils import create_causal_mask
24
+
25
+ from .inference_utils import find_last_equal_C
26
+ from .configuration_moss_tts import MossTTSDelayConfig
27
+
28
+ import math
29
+ import torch
30
+ import torch.nn as nn
31
+ import torch.nn.functional as F
32
+
33
+
34
+ class MossTTSRMSNorm(nn.Module):
35
+ def __init__(self, dim: int, eps: float = 1e-6):
36
+ super().__init__()
37
+ self.eps = eps
38
+ self.weight = nn.Parameter(torch.ones(dim))
39
+
40
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
41
+ # x: [..., dim]
42
+ norm = x.pow(2).mean(dim=-1, keepdim=True)
43
+ x = x * torch.rsqrt(norm + self.eps)
44
+ return x * self.weight
45
+
46
+
47
+ class MossTTSMLP(nn.Module):
48
+ """
49
+ HF-style MLP adapter equivalent to Megatron's SwiGLU FFN:
50
+ in: input_size
51
+ mid: ffn_hidden_size
52
+ out: output_size
53
+
54
+ Computes:
55
+ y = down( silu(gate(x)) * up(x) )
56
+
57
+ Optionally includes a pre-norm on input (common in Megatron blocks).
58
+ """
59
+ def __init__(
60
+ self,
61
+ input_size: int,
62
+ ffn_hidden_size: int,
63
+ output_size: int,
64
+ bias: bool = False,
65
+ prenorm: bool = False,
66
+ norm_eps: float = 1e-6,
67
+ use_rmsnorm: bool = True,
68
+ ):
69
+ super().__init__()
70
+
71
+ self.prenorm = prenorm
72
+ if prenorm:
73
+ if use_rmsnorm:
74
+ self.norm = MossTTSRMSNorm(input_size, eps=norm_eps)
75
+ else:
76
+ self.norm = nn.LayerNorm(input_size, eps=norm_eps)
77
+ else:
78
+ self.norm = None
79
+
80
+ # SwiGLU uses two projections to ffn_hidden_size: gate and up
81
+ self.gate_proj = nn.Linear(input_size, ffn_hidden_size, bias=bias)
82
+ self.up_proj = nn.Linear(input_size, ffn_hidden_size, bias=bias)
83
+
84
+ # down projection to output_size (note: output can differ from input)
85
+ self.down_proj = nn.Linear(ffn_hidden_size, output_size, bias=bias)
86
+
87
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
88
+ if self.norm is not None:
89
+ x = self.norm(x)
90
+
91
+ gate = self.gate_proj(x)
92
+ up = self.up_proj(x)
93
+ h = F.silu(gate) * up
94
+ y = self.down_proj(h)
95
+ return y
96
+
97
+ def moss_tts_masked_embedding(embedding: nn.Embedding,
98
+ input_ids: torch.LongTensor,
99
+ ignore_index: int = -100) -> torch.Tensor:
100
+ """
101
+ 对 input_ids 中 != ignore_index 的位置做 embedding,ignore_index 的位置输出全 0 向量。
102
+
103
+ Args:
104
+ embedding: 一个 nn.Embedding 层
105
+ input_ids: 任意形状的 LongTensor,里面允许出现 ignore_index
106
+ ignore_index: 需要被忽略的位置标记(默认 -100)
107
+
108
+ Returns:
109
+ embeddings: 形状为 (*input_ids.shape, embedding.embedding_dim) 的张量
110
+ """
111
+ # mask: True 表示需要正常 embedding,False 表示输出 0
112
+ mask = (input_ids != ignore_index) # shape: [...]
113
+
114
+ # 为了避免 -100 这种非法 index 传进 embedding,这里先临时替换掉
115
+ safe_ids = input_ids.clone()
116
+ safe_ids[~mask] = 0
117
+
118
+ # 正常过 embedding
119
+ out = embedding(safe_ids) # shape: [..., dim]
120
+
121
+ # 把 ignore_index 对应的位置置 0
122
+ # NOTE: out-of-place mask (was `out[~mask] = 0.0`) — the in-place variant raises
123
+ # "a leaf Variable that requires grad is being used in an in-place operation" once
124
+ # LoRA + gradient-checkpointing make the embedding output track grad.
125
+ out = out * mask.unsqueeze(-1).to(out.dtype)
126
+
127
+ return out
128
+
129
+ class MossTTSAttentionWithoutPositionalEmbedding(Qwen3Attention):
130
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
131
+
132
+ def __init__(self, config: MossTTSDelayConfig, layer_idx: int):
133
+ super().__init__(config, layer_idx)
134
+
135
+
136
+ def forward(
137
+ self,
138
+ hidden_states: torch.Tensor,
139
+ position_embeddings: Tuple[torch.Tensor, torch.Tensor],
140
+ attention_mask: Optional[torch.Tensor],
141
+ past_key_value: Optional[Cache] = None,
142
+ cache_position: Optional[torch.LongTensor] = None,
143
+ **kwargs,
144
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
145
+ input_shape = hidden_states.shape[:-1]
146
+ hidden_shape = (*input_shape, -1, self.head_dim)
147
+
148
+ query_states = self.q_norm(self.q_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
149
+ key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
150
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
151
+
152
+ assert past_key_value is None
153
+
154
+ attention_interface = eager_attention_forward
155
+ if self.config._attn_implementation != "eager":
156
+ if self.config._attn_implementation == "sdpa" and kwargs.get("output_attentions", False):
157
+ print(
158
+ "`torch.nn.functional.scaled_dot_product_attention` does not support `output_attentions=True`. Falling back to "
159
+ 'eager attention. This warning can be removed using the argument `attn_implementation="eager"` when loading the model.'
160
+ )
161
+ else:
162
+ attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
163
+
164
+ attn_output, attn_weights = attention_interface(
165
+ self,
166
+ query_states,
167
+ key_states,
168
+ value_states,
169
+ is_causal=True,
170
+ attention_mask=None,
171
+ dropout=0.0 if not self.training else self.attention_dropout,
172
+ scaling=self.scaling,
173
+ sliding_window=self.sliding_window, # diff with Llama
174
+ **kwargs,
175
+ )
176
+
177
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
178
+ attn_output = self.o_proj(attn_output)
179
+ return attn_output, attn_weights
180
+
181
+ class MossTTSLocalTransformer(Qwen3Model):
182
+ def __init__(self, config: MossTTSDelayConfig):
183
+ super().__init__(config)
184
+ del self.rotary_emb
185
+ del self.embed_tokens
186
+ for layer_idx in range(config.num_hidden_layers):
187
+ self.layers[layer_idx].self_attn = MossTTSAttentionWithoutPositionalEmbedding(config, layer_idx)
188
+ self.post_init()
189
+
190
+ def forward(
191
+ self,
192
+ input_ids: Optional[torch.LongTensor] = None,
193
+ attention_mask: Optional[torch.Tensor] = None,
194
+ position_ids: Optional[torch.LongTensor] = None,
195
+ past_key_values: Optional[Cache] = None,
196
+ inputs_embeds: Optional[torch.FloatTensor] = None,
197
+ use_cache: Optional[bool] = None,
198
+ output_attentions: Optional[bool] = None,
199
+ output_hidden_states: Optional[bool] = None,
200
+ cache_position: Optional[torch.LongTensor] = None,
201
+ **flash_attn_kwargs,
202
+ ) -> BaseModelOutputWithPast:
203
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
204
+ output_hidden_states = (
205
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
206
+ )
207
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
208
+ use_cache = False
209
+ assert not use_cache
210
+
211
+ if (input_ids is None) ^ (inputs_embeds is not None):
212
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
213
+
214
+ if self.gradient_checkpointing and self.training and use_cache:
215
+ print(
216
+ "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`."
217
+ )
218
+ use_cache = False
219
+
220
+ # TODO (joao): remove this exception in v4.56 -- it exists for users that try to pass a legacy cache
221
+ if not isinstance(past_key_values, (type(None), Cache)):
222
+ raise ValueError("The `past_key_values` should be either a `Cache` object or `None`.")
223
+
224
+ if inputs_embeds is None:
225
+ inputs_embeds = self.embed_tokens(input_ids)
226
+
227
+ if use_cache and past_key_values is None:
228
+ assert False
229
+ past_key_values = DynamicCache()
230
+
231
+ if cache_position is None:
232
+ past_seen_tokens = past_key_values.get_seq_length() if past_key_values is not None else 0
233
+ cache_position = torch.arange(
234
+ past_seen_tokens, past_seen_tokens + inputs_embeds.shape[1], device=inputs_embeds.device
235
+ )
236
+
237
+ if position_ids is None:
238
+ position_ids = cache_position.unsqueeze(0)
239
+
240
+ # causal_mask = self._update_causal_mask( # ???
241
+ # attention_mask, inputs_embeds, cache_position, past_key_values, output_attentions
242
+ # )
243
+ mask_kwargs = {
244
+ "config": self.config,
245
+ "input_embeds": inputs_embeds,
246
+ "attention_mask": attention_mask,
247
+ "cache_position": cache_position,
248
+ "past_key_values": past_key_values,
249
+ "position_ids": position_ids,
250
+ }
251
+ causal_mask = create_causal_mask(**mask_kwargs)
252
+
253
+
254
+ hidden_states = inputs_embeds
255
+
256
+ # create position embeddings to be shared across the decoder layers
257
+ # position_embeddings = self.rotary_emb(hidden_states, position_ids)
258
+
259
+ # decoder layers
260
+ all_hidden_states = () if output_hidden_states else None
261
+ all_self_attns = () if output_attentions else None
262
+
263
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
264
+ if output_hidden_states:
265
+ all_hidden_states += (hidden_states,)
266
+
267
+ layer_outputs = decoder_layer(
268
+ hidden_states,
269
+ attention_mask=causal_mask,
270
+ position_ids=None,
271
+ past_key_value=None,
272
+ output_attentions=output_attentions,
273
+ use_cache=use_cache,
274
+ cache_position=None,
275
+ position_embeddings=None,
276
+ **flash_attn_kwargs,
277
+ )
278
+
279
+ hidden_states = layer_outputs
280
+
281
+ if output_attentions:
282
+ all_self_attns += (layer_outputs[1],)
283
+
284
+ hidden_states = self.norm(hidden_states)
285
+
286
+ # add hidden states from the last decoder layer
287
+ if output_hidden_states:
288
+ all_hidden_states += (hidden_states,)
289
+
290
+ return BaseModelOutputWithPast(
291
+ last_hidden_state=hidden_states,
292
+ past_key_values=past_key_values if use_cache else None,
293
+ hidden_states=all_hidden_states,
294
+ attentions=all_self_attns,
295
+ )
296
+
297
+ @dataclass
298
+ class MosiTTSOutputWithPast(ModelOutput):
299
+ loss: Optional[torch.FloatTensor] = None
300
+ logits: torch.FloatTensor = None
301
+ loss_all: Optional[Tuple[torch.FloatTensor]] = None
302
+ logits_all: Optional[Tuple[torch.FloatTensor]] = None
303
+ past_key_values: Optional[Tuple[Tuple[torch.FloatTensor]]] = None
304
+ hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None
305
+ attentions: Optional[Tuple[torch.FloatTensor, ...]] = None
306
+
307
+
308
+ @dataclass
309
+ class MossTTSGenerateDecoderOnlyOutput(ModelOutput):
310
+ sequences: torch.LongTensor = None
311
+ scores: Optional[Tuple[torch.FloatTensor]] = None
312
+ logits: Optional[Tuple[torch.FloatTensor]] = None
313
+ attentions: Optional[Tuple[Tuple[torch.FloatTensor]]] = None
314
+ hidden_states: Optional[Tuple[Tuple[torch.FloatTensor]]] = None
315
+ past_key_values: Optional[Tuple[Tuple[Tuple[torch.FloatTensor]]]] = None
316
+
317
+
318
+ class CustomMixin(GenerationMixin): # TODO 待检查正确性
319
+ def _sample(
320
+ self,
321
+ input_ids: torch.LongTensor, # (B, T, 1+Nq)
322
+ logits_processor: LogitsProcessorList,
323
+ stopping_criteria: StoppingCriteriaList,
324
+ generation_config: GenerationConfig,
325
+ synced_gpus: bool,
326
+ streamer: Optional["BaseStreamer"] = None,
327
+ **model_kwargs,
328
+ ) -> Union[MossTTSGenerateDecoderOnlyOutput, torch.LongTensor]:
329
+ # 提取配置参数
330
+ # assert False
331
+ speech_pad_idx = self.config.audio_pad_code
332
+ device = input_ids.device
333
+ eos_token_id = generation_config.eos_token_id
334
+ output_attentions = generation_config.output_attentions
335
+ output_hidden_states = generation_config.output_hidden_states
336
+ output_scores = generation_config.output_scores
337
+ output_logits = generation_config.output_logits
338
+ return_dict_in_generate = generation_config.return_dict_in_generate
339
+ max_length = generation_config.max_length
340
+ has_eos_stopping_criteria = any(hasattr(criteria, "eos_token_id") for criteria in stopping_criteria)
341
+ do_sample = generation_config.do_sample
342
+
343
+ # 初始化输出元组
344
+ scores = () if (return_dict_in_generate and output_scores) else None
345
+ raw_logits = () if (return_dict_in_generate and output_logits) else None
346
+ decoder_attentions = () if (return_dict_in_generate and output_attentions) else None
347
+ decoder_hidden_states = () if (return_dict_in_generate and output_hidden_states) else None
348
+
349
+ # 初始化跟踪变量
350
+ batch_size, cur_len, channels = input_ids.shape # channels = 8
351
+ input_ids_length = cur_len
352
+ # assert batch_size == 1
353
+ this_peer_finished = False
354
+ unfinished_sequences = torch.ones(batch_size, dtype=torch.long, device=input_ids.device) # (B, )
355
+ base_length = input_ids.shape[1]
356
+ model_kwargs = self._get_initial_cache_position(cur_len, input_ids.device, model_kwargs)
357
+ # model_kwargs = self._get_initial_cache_position(input_ids, model_kwargs)
358
+
359
+ # 定义logits processor
360
+ if generation_config.do_samples is not None:
361
+ do_samples = generation_config.do_samples
362
+ realprocessor = [LogitsProcessorList() for _ in range(channels)]
363
+ for i, layer_config in enumerate(generation_config.layers):
364
+ if not do_samples[i]:
365
+ continue
366
+ if layer_config.get("repetition_penalty") is not None and i != 0: # 文本层不用重复惩罚
367
+ realprocessor[i].append(RepetitionPenaltyLogitsProcessor(penalty=layer_config.get("repetition_penalty")))
368
+ if layer_config.get("temperature") is not None:
369
+ realprocessor[i].append(TemperatureLogitsWarper(temperature=layer_config.get("temperature")))
370
+ if layer_config.get("top_k") is not None:
371
+ realprocessor[i].append(TopKLogitsWarper(top_k=layer_config.get("top_k")))
372
+ if layer_config.get("top_p") is not None:
373
+ realprocessor[i].append(TopPLogitsWarper(top_p=layer_config.get("top_p")))
374
+ else:
375
+ assert False
376
+ do_samples = [do_sample for _ in range(channels)]
377
+ realprocessor = [logits_processor for _ in range(channels)]
378
+
379
+ pbar = tqdm()
380
+ while self._has_unfinished_sequences(this_peer_finished, synced_gpus, device=input_ids.device):
381
+ # 准备模型输入
382
+ pbar.update()
383
+ model_inputs = self.prepare_inputs_for_generation(input_ids, **model_kwargs)
384
+ model_inputs.update({"output_attentions": output_attentions} if output_attentions else {})
385
+ model_inputs.update({"output_hidden_states": output_hidden_states} if output_hidden_states else {})
386
+ # 前向传递
387
+ outputs = self(**model_inputs, n_vq_for_inference=generation_config.n_vq_for_inference, return_dict=True, output_hidden_states=True)
388
+ model_kwargs = self._update_model_kwargs_for_generation(outputs, model_kwargs)
389
+
390
+ if synced_gpus and this_peer_finished:
391
+ continue
392
+
393
+ global_trm_output_hidden_states = outputs.hidden_states[-1][:, -1, :] # (B, D)
394
+ dtype = global_trm_output_hidden_states.dtype
395
+
396
+ local_trm_dim = self.local_transformer_config.hidden_size
397
+ local_transformer_inputs = torch.zeros(batch_size, 0, local_trm_dim).to(device).to(dtype) # (B, 0 <= t <= Nq, D), 维护当前 local trm 的输入
398
+ current_local_transformer_input = self.speech_embedding_to_local_mlp(global_trm_output_hidden_states) # (B, D) 维护当前 timestamp 的 local trm 的输入,
399
+
400
+ next_tokens = [] # 1+Nq * (B, )
401
+ # n_vq_for_inference = int(os.environ['N_VQ_FOR_INFERENCE'])
402
+ n_vq_for_inference = generation_config.n_vq_for_inference
403
+ for layer_index in range(min(channels, 1 + n_vq_for_inference)):
404
+ local_transformer_inputs = torch.cat([local_transformer_inputs, current_local_transformer_input.unsqueeze(1)], dim=1) # (B, t, D)
405
+ local_transformer_outputs = self.local_transformer(
406
+ input_ids=None,
407
+ attention_mask=None,
408
+ inputs_embeds=local_transformer_inputs # (B, t=1+Nq, D)
409
+ )[0] # (B, t=1+Nq, D)
410
+ local_transformer_outputs = self.layer_norm_before_lm_heads[layer_index](
411
+ self.local_to_speech_embedding_mlps[layer_index](local_transformer_outputs) # (B, t=1+Nq, D)
412
+ ) # (B, t=1+Nq, D)
413
+
414
+ next_token_logit = self.lm_heads[layer_index](local_transformer_outputs[:, -1, :]) # (B, V)
415
+ if layer_index != 0:
416
+ next_token_logit[:, speech_pad_idx] = -torch.inf
417
+ next_token_score = realprocessor[layer_index](input_ids[..., layer_index], next_token_logit) # (B, V)
418
+
419
+ if do_samples[layer_index]:
420
+ channel_ntk = torch.multinomial(nn.functional.softmax(next_token_score, dim=-1), num_samples=1).squeeze(1) # (B, )
421
+ else:
422
+ channel_ntk = torch.argmax(next_token_score, dim=-1) # (B, )
423
+
424
+ next_tokens.append(channel_ntk) # 1+Nq * (B, )
425
+ current_local_transformer_input = self.model.embedding_list[layer_index](channel_ntk) # (B, D)
426
+ current_local_transformer_input = self.speech_embedding_to_local_mlp(current_local_transformer_input) # (B, D)
427
+
428
+ for layer_index in range(1 + n_vq_for_inference, channels):
429
+ next_tokens.append(torch.zeros((batch_size, )).to(torch.int).to(device))
430
+ next_tokens = torch.stack(next_tokens, dim=-1) # (B, 1+Nq)
431
+
432
+ if has_eos_stopping_criteria:
433
+ for i in range(channels):
434
+ pddp = eos_token_id if i == 0 else speech_pad_idx
435
+ next_tokens[:, i] = next_tokens[:, i] * unfinished_sequences + pddp * (1 - unfinished_sequences)
436
+
437
+ input_ids = torch.cat([input_ids, next_tokens[:, None, :]], dim=1) # (B, T, 1+Nq)
438
+ if streamer is not None:
439
+ streamer.put(next_tokens[:, 0].cpu())
440
+
441
+ stopping = stopping_criteria(input_ids[..., 0], scores)
442
+ # stopping = stopping_criteria(input_ids[..., 0], scores)
443
+ unfinished_sequences = unfinished_sequences & ~stopping
444
+ this_peer_finished = unfinished_sequences.max() == 0
445
+
446
+ if return_dict_in_generate:
447
+ if output_scores:
448
+ assert False
449
+ scores += (next_token_scores,)
450
+ if output_logits:
451
+ assert False
452
+ raw_logits += (next_token_logits,)
453
+ if output_attentions:
454
+ decoder_attentions += (outputs.attentions,)
455
+ if output_hidden_states:
456
+ decoder_hidden_states += (outputs.hidden_states,)
457
+
458
+ cur_len += 1
459
+ del outputs
460
+
461
+ if streamer is not None:
462
+ streamer.end()
463
+
464
+ if return_dict_in_generate:
465
+ return MossTTSGenerateDecoderOnlyOutput(
466
+ sequences=input_ids,
467
+ scores=scores,
468
+ logits=raw_logits,
469
+ attentions=decoder_attentions,
470
+ hidden_states=decoder_hidden_states,
471
+ past_key_values=model_kwargs.get("past_key_values"),
472
+ )
473
+ else:
474
+ start_indices = find_last_equal_C(input_ids[..., 0], self.config.audio_start_token_id)
475
+ start_lengths = input_ids_length - start_indices - 1 # voice clone 下是 0,续写情况下是 prompt 音频的长度,不包括 audio_start_token
476
+ output = []
477
+ for start_idx, start_length, cur_generation_ids in zip(start_indices, start_lengths, input_ids):
478
+ output.append((start_length, cur_generation_ids[start_idx:]))
479
+
480
+ return output
481
+
482
+
483
+ class MosiTTSPretrainedModel(PreTrainedModel):
484
+ config_class = MossTTSDelayConfig
485
+ base_model_prefix = "model"
486
+ supports_gradient_checkpointing = True
487
+ _no_split_modules = ["Qwen2DecoderLayer"]
488
+ _skip_keys_device_placement = ["past_key_values"]
489
+ _supports_flash_attn_2 = True
490
+ _supports_sdpa = True
491
+ _supports_flex_attn = True
492
+ _supports_cache_class = True
493
+ _supports_quantized_cache = True
494
+ _supports_static_cache = True
495
+ _supports_attention_backend = True
496
+
497
+
498
+ class MosiTTSModel(MosiTTSPretrainedModel):
499
+ def __init__(self, config: MossTTSDelayConfig):
500
+ super().__init__(config)
501
+ self.text_pad_idx = config.pad_token_id
502
+ self.speech_pad_idx = config.audio_pad_code
503
+ self.embedding_list = nn.ModuleList([])
504
+ self.embedding_list.append(nn.Embedding(config.vocab_size, config.hidden_size, self.text_pad_idx))
505
+ self.channels = 1 + config.n_vq
506
+ for _ in range(1, self.channels):
507
+ self.embedding_list.append(nn.Embedding(config.audio_vocab_size + 1, config.hidden_size, self.speech_pad_idx))
508
+
509
+ self.language_model = Qwen3Model(config.language_config)
510
+ self.post_init()
511
+ self.language_model.embed_tokens.requires_grad_(False)
512
+
513
+ def get_input_embeddings(self):
514
+ return self.embedding_list[0]
515
+
516
+ def set_input_embeddings(self, value: nn.Embedding):
517
+ self.embedding_list[0] = value
518
+
519
+ def _prepare_multi_modal_inputs(
520
+ self,
521
+ input_ids: torch.LongTensor,
522
+ n_vq_for_inference: Optional[int] = None,
523
+ **kwargs,
524
+ ) -> torch.FloatTensor:
525
+ """
526
+ Prepares multi-modal embeddings from input_ids of shape (batch_size, channels, sequence_length).
527
+ For channel 0: text + speech tokens, for channels 1 to channels-1: speech tokens padded with speech_pad_token.
528
+ """
529
+ batch_size, seq_length, channels = input_ids.shape
530
+ if channels != self.channels:
531
+ raise ValueError(f"Expected {self.channels} channels, got {channels}")
532
+
533
+ if n_vq_for_inference is None:
534
+ n_vq_for_inference = self.channels - 1
535
+ n_vq_for_inference = max(1, min(self.channels - 1, int(n_vq_for_inference)))
536
+
537
+ inputs_embeds = torch.zeros(batch_size, seq_length, self.config.hidden_size, device=input_ids.device, dtype=self.embedding_list[0].weight.dtype)
538
+ for i in range(min(channels, 1 + n_vq_for_inference)):
539
+ embed_layer = self.embedding_list[i]
540
+ channel_input = input_ids[...,i]
541
+ inputs_embeds += embed_layer(channel_input)
542
+
543
+ return inputs_embeds # (B, T, D)
544
+
545
+ def forward(
546
+ self,
547
+ input_ids: torch.LongTensor = None, # Shape: (batch_size, channels, sequence_length)
548
+ attention_mask: Optional[torch.Tensor] = None,
549
+ position_ids: Optional[torch.LongTensor] = None,
550
+ past_key_values: Optional[List[torch.FloatTensor]] = None,
551
+ inputs_embeds: Optional[torch.FloatTensor] = None,
552
+ use_cache: Optional[bool] = None,
553
+ output_attentions: Optional[bool] = None,
554
+ output_hidden_states: Optional[bool] = None,
555
+ return_dict: Optional[bool] = None,
556
+ cache_position: Optional[torch.LongTensor] = None,
557
+ **kwargs,
558
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
559
+
560
+ if (input_ids is None) ^ (inputs_embeds is not None):
561
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
562
+
563
+ if input_ids is not None:
564
+ inputs_embeds = self._prepare_multi_modal_inputs(input_ids, **kwargs) # (B, T, D)
565
+
566
+ outputs = self.language_model(
567
+ input_ids=None,
568
+ attention_mask=attention_mask,
569
+ position_ids=position_ids,
570
+ past_key_values=past_key_values,
571
+ inputs_embeds=inputs_embeds,
572
+ use_cache=use_cache,
573
+ output_attentions=output_attentions,
574
+ output_hidden_states=output_hidden_states,
575
+ return_dict=return_dict,
576
+ cache_position=cache_position,
577
+ )
578
+ return outputs
579
+
580
+
581
+ class MossTTSDelayModel(MosiTTSPretrainedModel, CustomMixin):
582
+ _tied_weights_keys = None
583
+ _tp_plan = {"lm_head": "colwise_rep"}
584
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
585
+
586
+ def __init__(self, config: MossTTSDelayConfig):
587
+ super().__init__(config)
588
+ self.model = MosiTTSModel(config)
589
+ self.channels = 1 + config.n_vq
590
+ self.weights = [1 for _ in range(self.channels)]
591
+ self.vocab_size = config.vocab_size
592
+
593
+ local_transformer_config = copy.deepcopy(config.language_config)
594
+ local_transformer_config.num_hidden_layers = config.local_num_layers
595
+ local_transformer_config.hidden_size = config.local_hidden_size
596
+ local_transformer_config.intermediate_size = config.local_ffn_hidden_size
597
+ self.local_transformer_config = local_transformer_config
598
+ self.local_transformer = MossTTSLocalTransformer(self.local_transformer_config)
599
+
600
+ self.speech_embedding_to_local_mlp = MossTTSMLP(
601
+ input_size=config.hidden_size,
602
+ ffn_hidden_size=config.additional_mlp_ffn_hidden_size,
603
+ output_size=config.local_hidden_size
604
+ )
605
+ self.local_to_speech_embedding_mlps = nn.ModuleList([
606
+ MossTTSMLP(
607
+ input_size=config.local_hidden_size,
608
+ ffn_hidden_size=config.additional_mlp_ffn_hidden_size,
609
+ output_size=config.hidden_size
610
+ )
611
+ for _ in range(self.channels)
612
+ ])
613
+
614
+ self.layer_norm_before_lm_heads = nn.ModuleList([
615
+ MossTTSRMSNorm(config.hidden_size)
616
+ for _ in range(self.channels)
617
+ ])
618
+
619
+ self.lm_heads = nn.ModuleList([])
620
+ self.lm_heads.append(nn.Linear(config.hidden_size, config.vocab_size, bias=False))
621
+ for _ in range(1, self.channels):
622
+ self.lm_heads.append(nn.Linear(config.hidden_size, 1 + config.audio_vocab_size, bias=False))
623
+ self.post_init()
624
+ self._freeze_unused_qwen_embeddings()
625
+ self.register_load_state_dict_post_hook(self._freeze_unused_qwen_embeddings_post_load)
626
+
627
+ @classmethod
628
+ def from_pretrained(cls, *args, **kwargs):
629
+ model = super().from_pretrained(*args, **kwargs)
630
+ model._freeze_unused_qwen_embeddings()
631
+ return model
632
+
633
+ def get_input_embeddings(self):
634
+ return self.model.embedding_list[0]
635
+
636
+ def _freeze_unused_qwen_embeddings(self) -> None:
637
+ self.model.language_model.embed_tokens.requires_grad_(False)
638
+
639
+ def _freeze_unused_qwen_embeddings_post_load(self, module, incompatible_keys) -> None:
640
+ module._freeze_unused_qwen_embeddings()
641
+
642
+ def can_generate(self):
643
+ return True
644
+
645
+ def _build_generation_config(
646
+ self,
647
+ generation_config: Optional[GenerationConfig] = None,
648
+ max_new_tokens: Optional[int] = None,
649
+ text_temperature: Optional[float] = None,
650
+ text_top_p: Optional[float] = None,
651
+ text_top_k: Optional[int] = None,
652
+ text_repetition_penalty: Optional[float] = None,
653
+ audio_temperature: Optional[float] = None,
654
+ audio_top_p: Optional[float] = None,
655
+ audio_top_k: Optional[int] = None,
656
+ audio_repetition_penalty: Optional[float] = None,
657
+ n_vq_for_inference: Optional[int] = None,
658
+ ) -> GenerationConfig:
659
+ config = copy.deepcopy(generation_config or self.generation_config)
660
+
661
+ text_temperature = 1.5 if text_temperature is None else float(text_temperature)
662
+ text_top_p = 1.0 if text_top_p is None else float(text_top_p)
663
+ text_top_k = 50 if text_top_k is None else int(text_top_k)
664
+ text_repetition_penalty = 1.0 if text_repetition_penalty is None else float(text_repetition_penalty)
665
+ audio_temperature = 1.0 if audio_temperature is None else float(audio_temperature)
666
+ audio_top_p = 0.95 if audio_top_p is None else float(audio_top_p)
667
+ audio_top_k = 50 if audio_top_k is None else int(audio_top_k)
668
+ audio_repetition_penalty = 1.1 if audio_repetition_penalty is None else float(audio_repetition_penalty)
669
+
670
+ text_do_sample = text_temperature > 0
671
+ if not text_do_sample:
672
+ text_temperature = 1.0
673
+ audio_do_sample = audio_temperature > 0
674
+ if not audio_do_sample:
675
+ audio_temperature = 1.0
676
+
677
+ if max_new_tokens is not None:
678
+ config.max_new_tokens = int(max_new_tokens)
679
+ elif getattr(config, "max_new_tokens", None) is None:
680
+ config.max_new_tokens = 100000 # about 2.2 hours , can be overridden by user input, you can set to a smaller value for faster generation during debugging
681
+
682
+ if getattr(config, "pad_token_id", None) is None:
683
+ config.pad_token_id = self.config.pad_token_id
684
+ config.eos_token_id = self.config.audio_end_token_id
685
+ config.use_cache = True
686
+ config.do_sample = text_do_sample or audio_do_sample
687
+
688
+ resolved_n_vq = self.channels - 1 if n_vq_for_inference is None else int(n_vq_for_inference)
689
+ resolved_n_vq = max(1, min(self.channels - 1, resolved_n_vq))
690
+ config.n_vq_for_inference = resolved_n_vq
691
+ config.do_samples = [text_do_sample] + [audio_do_sample] * (self.channels - 1)
692
+ config.layers = [
693
+ {
694
+ "repetition_penalty": text_repetition_penalty,
695
+ "temperature": text_temperature,
696
+ "top_p": text_top_p,
697
+ "top_k": text_top_k,
698
+ }
699
+ ] + [
700
+ {
701
+ "repetition_penalty": audio_repetition_penalty,
702
+ "temperature": audio_temperature,
703
+ "top_p": audio_top_p,
704
+ "top_k": audio_top_k,
705
+ }
706
+ for _ in range(self.channels - 1)
707
+ ]
708
+ return config
709
+
710
+ @torch.inference_mode()
711
+ def generate(
712
+ self,
713
+ input_ids: torch.LongTensor,
714
+ attention_mask: Optional[torch.Tensor] = None,
715
+ generation_config: Optional[GenerationConfig] = None,
716
+ max_new_tokens: Optional[int] = None,
717
+ text_temperature: Optional[float] = None,
718
+ text_top_p: Optional[float] = None,
719
+ text_top_k: Optional[int] = None,
720
+ text_repetition_penalty: Optional[int] = None,
721
+ audio_temperature: Optional[float] = None,
722
+ audio_top_p: Optional[float] = None,
723
+ audio_top_k: Optional[int] = None,
724
+ audio_repetition_penalty: Optional[float] = None,
725
+ n_vq_for_inference: Optional[int] = None,
726
+ **kwargs,
727
+ ):
728
+ resolved_generation_config = self._build_generation_config(
729
+ generation_config=generation_config,
730
+ max_new_tokens=max_new_tokens,
731
+ text_temperature=text_temperature,
732
+ text_top_p=text_top_p,
733
+ text_top_k=text_top_k,
734
+ text_repetition_penalty=text_repetition_penalty,
735
+ audio_temperature=audio_temperature,
736
+ audio_top_p=audio_top_p,
737
+ audio_top_k=audio_top_k,
738
+ audio_repetition_penalty=audio_repetition_penalty,
739
+ n_vq_for_inference=n_vq_for_inference,
740
+ )
741
+ return super().generate(
742
+ input_ids=input_ids,
743
+ attention_mask=attention_mask,
744
+ generation_config=resolved_generation_config,
745
+ **kwargs,
746
+ )
747
+
748
+ # def tie_weights(self):
749
+ # ...
750
+ # for i in range(self.config.channels):
751
+ # self._tie_or_clone_weights(self.lm_heads[i], self.model.embedding_list[i])
752
+
753
+ def set_input_embeddings(self, value):
754
+ self.model.embedding_list[0] = value
755
+
756
+ def get_output_embeddings(self):
757
+ return self.lm_heads[0]
758
+
759
+ def set_output_embeddings(self, new_embeddings):
760
+ self.lm_heads[0] = new_embeddings
761
+
762
+ def set_decoder(self, decoder):
763
+ self.model = decoder
764
+
765
+ def get_decoder(self):
766
+ return self.model
767
+
768
+ def set_weights(self, weights):
769
+ self.weights = weights
770
+
771
+ def _prepare_shifted_audio_inputs(self, label_ids): # (B, T, 1 + Nq) 可能有 -100
772
+ text_and_audio_label_embed_list = [] # Nq * (1, T, B, D)
773
+ for i in range(self.channels - 1):
774
+ text_and_audio_label_embed_list.append(
775
+ moss_tts_masked_embedding(self.model.embedding_list[i], label_ids[:, :, i]).unsqueeze(0).transpose(1, 2) # (B, T) -> (B, T, D) -> (1, B, T, D) -> (1, T, B, D)
776
+ ) # (1, T, B, D)
777
+ audio_label_embeds = torch.stack(text_and_audio_label_embed_list, dim=0) # (Nq, 1, T, B, D)
778
+ audio_label_embeds = audio_label_embeds.contiguous()[:, 0, :, :, :].transpose(1, 2) # (Nq, B, T, D)
779
+ return audio_label_embeds # (Nq, B, T, D)
780
+
781
+ def forward(
782
+ self,
783
+ input_ids: torch.LongTensor = None, # (B, T, 1 + Nq)
784
+ attention_mask: Optional[torch.Tensor] = None,
785
+ position_ids: Optional[torch.LongTensor] = None,
786
+ past_key_values: Optional[Union[Cache, List[torch.FloatTensor]]] = None,
787
+ inputs_embeds: Optional[torch.FloatTensor] = None,
788
+ labels: Optional[torch.LongTensor] = None, # (B, T, 1 + Nq), TODO labels 为 input_ids shift 一位的结果
789
+ channelwise_loss_weight: Optional[List[float]] = None,
790
+ use_cache: Optional[bool] = None,
791
+ output_attentions: Optional[bool] = None,
792
+ output_hidden_states: Optional[bool] = None,
793
+ return_dict: Optional[bool] = None,
794
+ cache_position: Optional[torch.LongTensor] = None,
795
+ **kwargs,
796
+ ) -> Union[Tuple, MosiTTSOutputWithPast]:
797
+ device = input_ids.device if not input_ids is None else inputs_embeds.device
798
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
799
+ output_hidden_states = output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
800
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
801
+
802
+ # decoder outputs consists of (dec_features, layer_state, dec_hidden, dec_attn)
803
+ outputs = self.model(
804
+ input_ids=input_ids, # (B, T, 1 + Nq)
805
+ attention_mask=attention_mask,
806
+ position_ids=position_ids,
807
+ past_key_values=past_key_values,
808
+ inputs_embeds=inputs_embeds,
809
+ use_cache=use_cache,
810
+ output_attentions=output_attentions,
811
+ output_hidden_states=output_hidden_states,
812
+ return_dict=return_dict,
813
+ cache_position=cache_position,
814
+ **kwargs,
815
+ )
816
+
817
+ if labels is not None:
818
+ local_transformer_inputs_from_global = outputs[0].unsqueeze(0) # (1, B, T, D)
819
+ D_global= local_transformer_inputs_from_global.shape[-1]
820
+ local_transformer_inputs_from_speech_embeddings = self._prepare_shifted_audio_inputs(labels) # (B, T, 1 + Nq) -> (Nq, B, T, D)
821
+ local_transformer_input_hidden_states = torch.cat([local_transformer_inputs_from_global, local_transformer_inputs_from_speech_embeddings], dim=0).contiguous() # (1 + Nq, B, T, D)
822
+ local_transformer_input_hidden_states = self.speech_embedding_to_local_mlp(local_transformer_input_hidden_states) # (1 + Nq, B, T, D)
823
+ N_channels, B, T, D_local = local_transformer_input_hidden_states.shape
824
+ local_transformer_input_hidden_states = local_transformer_input_hidden_states.permute(1, 2, 0, 3) # (B, T, 1 + Nq, D)
825
+ local_transformer_input_hidden_states = local_transformer_input_hidden_states.reshape(B * T, N_channels, D_local) # (batch_size=B * T, time=1+Nq, D)
826
+ local_transformer_output_hidden_states = self.local_transformer( # TODO 没有开位置编码
827
+ input_ids=None,
828
+ attention_mask=None,
829
+ inputs_embeds=local_transformer_input_hidden_states # (batch_size=B * T, time=1+Nq, D)
830
+ )[0] # (batch_size=B * T, time=1+Nq, D)
831
+ after_lm_head_mlp_hidden_states = [] # Nq+1 * (B*T, D) TODO ???
832
+ for i in range(self.channels):
833
+ after_lm_head_mlp_hidden_states.append(
834
+ self.layer_norm_before_lm_heads[i](
835
+ self.local_to_speech_embedding_mlps[i](
836
+ local_transformer_output_hidden_states[:, i, :] # (B*T, D)
837
+ )
838
+ )
839
+ ) # Nq+1 * (B*T, D)
840
+
841
+ after_lm_head_mlp_hidden_states = torch.stack(after_lm_head_mlp_hidden_states, dim=0) # (1 + Nq, B*T, D)
842
+ after_lm_head_mlp_hidden_states = after_lm_head_mlp_hidden_states.reshape(N_channels, B, T, D_global) # (1 + Nq, B, T, D)
843
+ logits_all = [lm_head(h_i) for lm_head, h_i in zip(self.lm_heads, after_lm_head_mlp_hidden_states)] # 1+Nq * (B, T, V)
844
+
845
+ loss_all = torch.empty(self.channels, device=device) # (1 + Nq)
846
+
847
+ for i in range(self.channels):
848
+ vocab_size = self.config.vocab_size if i == 0 else (1 + self.config.audio_vocab_size)
849
+ loss_all[i] = ForCausalLMLoss(logits_all[i], labels[..., i], vocab_size, shift_labels=labels[..., i]) # (B, T, V), (B, T) => (1, )
850
+ effective_weights = channelwise_loss_weight if channelwise_loss_weight is not None else self.weights
851
+ if len(effective_weights) != self.channels:
852
+ raise ValueError(
853
+ f"`channelwise_loss_weight` length {len(effective_weights)} != {self.channels}."
854
+ )
855
+ total_weight = float(sum(effective_weights))
856
+ if total_weight <= 0:
857
+ raise ValueError("`channelwise_loss_weight` must sum to a positive value.")
858
+ normalized_weights = [float(weight_i) / total_weight for weight_i in effective_weights] # (1+Nq, )
859
+
860
+ total_loss = 0
861
+ for w, loss in zip(normalized_weights, loss_all):
862
+ total_loss += w * loss
863
+ else:
864
+ total_loss = None
865
+ loss_all = None
866
+ logits_all = [None] * self.channels
867
+
868
+ if not return_dict:
869
+ output = (logits_all,) + outputs[1:]
870
+ return (total_loss, loss_all) + output if total_loss is not None else output
871
+
872
+ return MosiTTSOutputWithPast(
873
+ loss=total_loss,
874
+ logits=logits_all[0],
875
+ loss_all=loss_all,
876
+ logits_all=logits_all, # 1+Nq * (B, T, V)
877
+ past_key_values=outputs.past_key_values,
878
+ hidden_states=outputs.hidden_states, # L * (B, T, D)
879
+ attentions=outputs.attentions,
880
+ )
processing_moss_tts.py ADDED
@@ -0,0 +1,950 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2026 OpenMOSS and the HuggingFace Inc. team. All rights reserved.
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
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import os
17
+ from typing import Any, Dict, List, Optional, Tuple, Type, Union, Literal, Final, cast
18
+ from dataclasses import dataclass
19
+ from pathlib import Path
20
+ import re
21
+ import torchaudio
22
+
23
+ from transformers import processing_utils
24
+
25
+ processing_utils.MODALITY_TO_BASE_CLASS_MAPPING["audio_tokenizer"] = "PreTrainedModel"
26
+
27
+ import torch
28
+ from transformers import (
29
+ PreTrainedTokenizerBase,
30
+ BatchFeature,
31
+ ProcessorMixin,
32
+ logging,
33
+ AutoConfig,
34
+ AutoModel,
35
+ AutoTokenizer,
36
+ )
37
+
38
+ from .configuration_moss_tts import MossTTSDelayConfig
39
+
40
+
41
+ logger = logging.get_logger(__name__)
42
+
43
+
44
+ AUDIO_PLACEHOLDER = "<|audio|>"
45
+
46
+
47
+ @dataclass
48
+ class Message:
49
+ def to_dict(self) -> Dict[str, Any]:
50
+ raise NotImplementedError
51
+
52
+
53
+ @dataclass
54
+ class UserMessage(Message):
55
+ text: Optional[str] = None
56
+ reference: Optional[List[Optional[Union[str, torch.Tensor]]]] = None
57
+ instruction: Optional[str] = None
58
+ tokens: Optional[int] = None
59
+ quality: Optional[str] = None
60
+ sound_event: Optional[str] = None
61
+ ambient_sound: Optional[str] = None
62
+ language: Optional[str] = None
63
+
64
+ def __post_init__(self):
65
+ template = """<user_inst>
66
+ - Reference(s):
67
+ {reference}
68
+ - Instruction:
69
+ {instruction}
70
+ - Tokens:
71
+ {tokens}
72
+ - Quality:
73
+ {quality}
74
+ - Sound Event:
75
+ {sound_event}
76
+ - Ambient Sound:
77
+ {ambient_sound}
78
+ - Language:
79
+ {language}
80
+ - Text:
81
+ {text}
82
+ </user_inst>"""
83
+
84
+ audio_codes_list = []
85
+ if self.reference is None:
86
+ reference = "None"
87
+ elif isinstance(self.reference, List):
88
+ reference = []
89
+ for speaker_idx, speaker_reference in enumerate(self.reference):
90
+ if speaker_reference is not None:
91
+ reference.append(f"[S{speaker_idx+1}]:\n{AUDIO_PLACEHOLDER}")
92
+ reference = "\n".join(reference)
93
+ audio_codes_list = [
94
+ speaker_reference
95
+ for speaker_reference in self.reference
96
+ if speaker_reference is not None
97
+ ]
98
+ else:
99
+ raise TypeError("`reference` should be exactly a list when it is not None.")
100
+
101
+ content = (
102
+ template.replace("{reference}", str(reference))
103
+ .replace("{instruction}", str(self.instruction))
104
+ .replace("{tokens}", str(self.tokens))
105
+ .replace("{quality}", str(self.quality))
106
+ .replace("{sound_event}", str(self.sound_event))
107
+ .replace("{ambient_sound}", str(self.ambient_sound))
108
+ .replace("{language}", str(self.language))
109
+ .replace("{text}", str(self.text))
110
+ )
111
+
112
+ self._content = content
113
+ self._audio_codes_list = audio_codes_list
114
+
115
+ def to_dict(self):
116
+ return {
117
+ "role": "user",
118
+ "content": self._content,
119
+ "audio_codes_list": self._audio_codes_list,
120
+ }
121
+
122
+
123
+ @dataclass
124
+ class AssistantMessage(Message):
125
+ audio_codes_list: List[Union[str, torch.Tensor]]
126
+ content: str = AUDIO_PLACEHOLDER
127
+
128
+ def to_dict(self):
129
+ return {
130
+ "role": "assistant",
131
+ "content": self.content,
132
+ "audio_codes_list": self.audio_codes_list,
133
+ }
134
+
135
+
136
+ USER_MESSAGE_FIELDS = (
137
+ "text",
138
+ "reference",
139
+ "instruction",
140
+ "tokens",
141
+ "quality",
142
+ "sound_event",
143
+ "ambient_sound",
144
+ "language",
145
+ )
146
+
147
+
148
+ class MossTTSDelayProcessor(ProcessorMixin):
149
+ tokenizer_class = "AutoTokenizer"
150
+ audio_tokenizer_class = "AutoModel"
151
+
152
+ tokenizer: PreTrainedTokenizerBase
153
+ audio_tokenizer: Any
154
+
155
+ def __init__(
156
+ self,
157
+ tokenizer: PreTrainedTokenizerBase,
158
+ audio_tokenizer: Any = None,
159
+ model_config: Optional[MossTTSDelayConfig] = None,
160
+ **kwargs,
161
+ ):
162
+ super().__init__(tokenizer=tokenizer, audio_tokenizer=audio_tokenizer, **kwargs)
163
+
164
+ # Explicit assignments for type-checkers; ProcessorMixin sets these too.
165
+ self.tokenizer = tokenizer
166
+ self.audio_tokenizer = audio_tokenizer
167
+ if model_config is None:
168
+ model_config = MossTTSDelayConfig()
169
+ self.model_config = model_config
170
+
171
+ self.imstart_token_id = tokenizer.convert_tokens_to_ids("<|im_start|>")
172
+ self.imend_token_id = tokenizer.convert_tokens_to_ids("<|im_end|>")
173
+ self.newline_token_id = 198
174
+
175
+ def _id_to_token(token_id: int) -> str:
176
+ tok = tokenizer.convert_ids_to_tokens(int(token_id))
177
+ if isinstance(tok, list):
178
+ return tok[0] if len(tok) > 0 else ""
179
+ return cast(str, tok)
180
+
181
+ self.audio_user_slot_token = _id_to_token(
182
+ self.model_config.audio_user_slot_token_id
183
+ )
184
+ self.audio_assistant_gen_slot_token = _id_to_token(
185
+ self.model_config.audio_assistant_gen_slot_token_id
186
+ )
187
+ self.audio_assistant_delay_slot_token = _id_to_token(
188
+ self.model_config.audio_assistant_delay_slot_token_id
189
+ )
190
+ self.audio_start_token = _id_to_token(self.model_config.audio_start_token_id)
191
+ self.audio_end_token = _id_to_token(self.model_config.audio_end_token_id)
192
+
193
+ @classmethod
194
+ def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs):
195
+ trust_remote_code = kwargs.pop("trust_remote_code", True)
196
+ kwargs.pop("_from_auto", None)
197
+
198
+ audio_tokenizer_name_or_path = kwargs.pop("codec_path", None)
199
+ if audio_tokenizer_name_or_path is None:
200
+ processor_lookup_kwargs = dict(kwargs)
201
+ try:
202
+ processor_dict, _ = cls.get_processor_dict(
203
+ pretrained_model_name_or_path,
204
+ **processor_lookup_kwargs,
205
+ )
206
+ audio_tokenizer_name_or_path = processor_dict.get(
207
+ "audio_tokenizer_name_or_path"
208
+ )
209
+ audio_tokenizer_dict = processor_dict.get("audio_tokenizer", {})
210
+ if isinstance(audio_tokenizer_dict, dict):
211
+ audio_tokenizer_name_or_path = audio_tokenizer_dict.get(
212
+ "audio_tokenizer_name_or_path"
213
+ ) or audio_tokenizer_name_or_path
214
+ except Exception:
215
+ audio_tokenizer_name_or_path = None
216
+ if audio_tokenizer_name_or_path is None:
217
+ audio_tokenizer_name_or_path = "OpenMOSS-Team/MOSS-Audio-Tokenizer"
218
+
219
+ pretrained_model_name_or_path = str(pretrained_model_name_or_path)
220
+ local_model_path = Path(pretrained_model_name_or_path)
221
+ pretrained_model_ref = local_model_path if local_model_path.exists() else pretrained_model_name_or_path
222
+ model_config = cast(
223
+ MossTTSDelayConfig,
224
+ AutoConfig.from_pretrained(
225
+ pretrained_model_ref,
226
+ *args,
227
+ trust_remote_code=trust_remote_code,
228
+ **kwargs,
229
+ ),
230
+ )
231
+ tokenizer = AutoTokenizer.from_pretrained(
232
+ pretrained_model_ref,
233
+ *args,
234
+ trust_remote_code=trust_remote_code,
235
+ **kwargs,
236
+ )
237
+ audio_tokenizer = AutoModel.from_pretrained(
238
+ audio_tokenizer_name_or_path,
239
+ trust_remote_code=trust_remote_code,
240
+ **kwargs,
241
+ )
242
+
243
+ return cls(
244
+ tokenizer=tokenizer,
245
+ audio_tokenizer=audio_tokenizer,
246
+ model_config=model_config,
247
+ **kwargs,
248
+ )
249
+
250
+ def __call__(self, *args, **kwargs) -> BatchFeature:
251
+ conversations = args[0] if len(args) > 0 else kwargs.pop("conversations")
252
+ mode: str = kwargs.pop("mode", "generation")
253
+ apply_chat_template: bool = kwargs.pop("apply_chat_template", True)
254
+ n_vq: Optional[int] = kwargs.pop("n_vq", None)
255
+
256
+ # Common ProcessorMixin kwargs that we ignore because we always return torch tensors.
257
+ kwargs.pop("return_tensors", None)
258
+ kwargs.pop("padding", None)
259
+ kwargs.pop("truncation", None)
260
+
261
+ """
262
+ mode only works when a Message is converted to a dict.
263
+ """
264
+
265
+ if mode not in {"generation", "continuation", "computing_loss"}:
266
+ raise RuntimeError
267
+
268
+ if isinstance(conversations, (Message, Dict)):
269
+ conversations = [conversations]
270
+
271
+ truncation = False
272
+ if mode == "continuation":
273
+ truncation = True
274
+
275
+ input_ids_list = []
276
+ for conversation in conversations:
277
+ if isinstance(conversation, (Message, Dict)):
278
+ conversation = [conversation]
279
+
280
+ # Normalize early so downstream logic always deals with dict messages.
281
+ conversation = [self._normalize_message(m) for m in conversation]
282
+
283
+ if (mode == "generation") ^ (len(conversation) % 2 != 0):
284
+ raise ValueError
285
+
286
+ if (mode == "generation") ^ (conversation[-1]["role"] == "user"):
287
+ raise ValueError
288
+
289
+ unified_codes = []
290
+ for message_idx, message in enumerate(conversation):
291
+ if apply_chat_template:
292
+ add_generation_prompt = (
293
+ mode == "generation" and message_idx == len(conversation) - 1
294
+ )
295
+ try:
296
+ content = self.tokenizer.apply_chat_template(
297
+ [{"role": message["role"], "content": message["content"]}],
298
+ add_generation_prompt=add_generation_prompt,
299
+ tokenize=False,
300
+ )
301
+ except TypeError:
302
+ try:
303
+ content = self.tokenizer.apply_chat_template(
304
+ [
305
+ {
306
+ "role": message["role"],
307
+ "content": message["content"],
308
+ }
309
+ ],
310
+ add_generation_prompt=add_generation_prompt,
311
+ )
312
+ except Exception:
313
+ logger.warning(
314
+ "apply_chat_template failed; fallback to raw content."
315
+ )
316
+ content = message["content"]
317
+ else:
318
+ content = message["content"]
319
+
320
+ if not isinstance(content, str):
321
+ content = str(content)
322
+
323
+ # Batch-encode all path-based references in one call when possible.
324
+ # This ensures we actually exercise audio_tokenizer.batch_encode for multi-reference prompts,
325
+ # instead of repeatedly calling it with batch=1.
326
+ raw_audio_items = message.get("audio_codes_list", [])
327
+
328
+ audio_codes_list: List[torch.Tensor] = []
329
+ if len(raw_audio_items) > 0:
330
+ encoded_items: List[Optional[torch.Tensor]] = [None] * len(
331
+ raw_audio_items
332
+ )
333
+ paths: List[str] = []
334
+ path_positions: List[int] = []
335
+
336
+ for idx, item in enumerate(raw_audio_items):
337
+ if isinstance(item, torch.Tensor):
338
+ if n_vq is not None and item.shape[1] != n_vq:
339
+ raise RuntimeError(
340
+ "audio_codes's n_vq is not equal to the parameter `n_vq`. Your can set the parameter `n_vq` as None if you have already tokenzied the wavs."
341
+ )
342
+ encoded_items[idx] = item
343
+ continue
344
+
345
+ if isinstance(item, (str, os.PathLike)):
346
+ paths.append(str(item))
347
+ path_positions.append(idx)
348
+ continue
349
+
350
+ raise TypeError(
351
+ "Each audio item must be a torch.Tensor of codes or a path-like string."
352
+ )
353
+
354
+ if len(paths) > 0:
355
+ encoded_from_paths = self.encode_audios_from_path(paths, n_vq)
356
+ if len(encoded_from_paths) != len(paths):
357
+ raise RuntimeError(
358
+ "encode_audios_from_path returned an unexpected number of items."
359
+ )
360
+ for pos, codes in zip(path_positions, encoded_from_paths):
361
+ encoded_items[pos] = codes
362
+
363
+ audio_codes_list = [cast(torch.Tensor, t) for t in encoded_items]
364
+ unified_codes.append(
365
+ self._get_unified_codes(
366
+ message["role"], content, audio_codes_list, truncation
367
+ )
368
+ )
369
+
370
+ unified_codes = torch.cat(unified_codes) # (T, Nq)
371
+ if mode == "generation":
372
+ audio_start_position_tokens = torch.zeros((1, unified_codes.shape[-1])).to(unified_codes.dtype).to(unified_codes.device) # (1, Nq)
373
+ audio_start_position_tokens[:, 0] = self.tokenizer.encode(self.audio_start_token)[0]
374
+ audio_start_position_tokens[:, 1:] = self.model_config.audio_pad_code
375
+ unified_codes = torch.cat([unified_codes, audio_start_position_tokens], dim=0) # (T, Nq)
376
+ input_ids_list.append(unified_codes)
377
+
378
+ return BatchFeature(data=self._pad(input_ids_list))
379
+
380
+ @staticmethod
381
+ def build_user_message(
382
+ text: Optional[str] = None,
383
+ reference: Optional[List[Optional[Union[str, torch.Tensor]]]] = None,
384
+ instruction: Optional[str] = None,
385
+ tokens: Optional[int] = None,
386
+ quality: Optional[str] = None,
387
+ sound_event: Optional[str] = None,
388
+ ambient_sound: Optional[str] = None,
389
+ language: Optional[str] = None,
390
+ ) -> Dict:
391
+ if reference is not None and not isinstance(reference, list):
392
+ reference = [reference]
393
+ return UserMessage(
394
+ text=text,
395
+ reference=reference,
396
+ instruction=instruction,
397
+ tokens=tokens,
398
+ quality=quality,
399
+ sound_event=sound_event,
400
+ ambient_sound=ambient_sound,
401
+ language=language,
402
+ ).to_dict()
403
+
404
+ @staticmethod
405
+ def build_assistant_message(
406
+ audio_codes_list: List[Union[str, torch.Tensor]],
407
+ content: str = AUDIO_PLACEHOLDER,
408
+ ) -> Dict:
409
+ return AssistantMessage(
410
+ audio_codes_list=audio_codes_list,
411
+ content=content,
412
+ ).to_dict()
413
+
414
+ def _normalize_message(self, message: Union[Message, Dict]) -> Dict:
415
+ if isinstance(message, Message):
416
+ return message.to_dict()
417
+ if not isinstance(message, dict):
418
+ raise TypeError("Each message must be a Message or dict.")
419
+ if "role" not in message:
420
+ raise ValueError("Message dict must include a 'role' field.")
421
+ if "content" in message and "audio_codes_list" in message:
422
+ return message
423
+ role = message["role"]
424
+ if role == "user":
425
+ kwargs = {key: message.get(key) for key in USER_MESSAGE_FIELDS}
426
+ return self.build_user_message(**kwargs)
427
+ if role == "assistant":
428
+ return self.build_assistant_message(
429
+ audio_codes_list=message.get("audio_codes_list", []),
430
+ content=message.get("content", AUDIO_PLACEHOLDER),
431
+ )
432
+ raise ValueError(f"Unsupported role: {role}")
433
+
434
+ def _pad(self, input_ids_list: List[torch.Tensor]):
435
+ device = input_ids_list[0].device
436
+ lengths = torch.tensor([w.shape[0] for w in input_ids_list], device=device)
437
+ pad_input_ids = torch.nn.utils.rnn.pad_sequence(
438
+ input_ids_list,
439
+ batch_first=True,
440
+ padding_value=self.model_config.audio_pad_code,
441
+ padding_side="left",
442
+ )
443
+ other_channel_mask = (pad_input_ids.shape[1] - lengths).unsqueeze(
444
+ 1
445
+ ) > torch.arange(pad_input_ids.shape[1], device=device).unsqueeze(0)
446
+ pad_input_ids[..., 0][other_channel_mask] = self.model_config.pad_token_id
447
+ attention_mask = torch.zeros(
448
+ pad_input_ids.shape[0], pad_input_ids.shape[1], device=device
449
+ )
450
+ attention_mask[~other_channel_mask] = 1
451
+ attention_mask = attention_mask.bool()
452
+ return {
453
+ "input_ids": pad_input_ids, # [batch_size, seqlen, n_vq]
454
+ "attention_mask": attention_mask,
455
+ }
456
+
457
+ @staticmethod
458
+ def _replace_audio_placeholders(
459
+ content: str,
460
+ lengths: List[int],
461
+ n_vq: int,
462
+ gen_slot_token: str,
463
+ delay_slot_token: str,
464
+ audio_start_token: str,
465
+ audio_end_token: str,
466
+ ) -> str:
467
+ if n_vq < 1:
468
+ raise ValueError(f"n_vq must be >= 1, got {n_vq}")
469
+
470
+ num_placeholders = content.count(AUDIO_PLACEHOLDER)
471
+ if num_placeholders != len(lengths):
472
+ raise ValueError(
473
+ f"Number of {AUDIO_PLACEHOLDER} ({num_placeholders}) "
474
+ f"does not match lengths ({len(lengths)})"
475
+ )
476
+
477
+ def build_audio_block(length: int) -> str:
478
+ if length < 0:
479
+ raise ValueError(f"length must be >= 0, got {length}")
480
+
481
+ if length == 0:
482
+ return f"{audio_start_token}{audio_end_token}"
483
+
484
+ step_tokens = gen_slot_token * length
485
+ return f"{audio_start_token}{step_tokens}{audio_end_token}"
486
+
487
+ lengths_iter = iter(lengths)
488
+
489
+ def replacer(match: re.Match) -> str:
490
+ length = next(lengths_iter)
491
+ return build_audio_block(length)
492
+
493
+ result = re.sub(re.escape(AUDIO_PLACEHOLDER), replacer, content)
494
+
495
+ return result
496
+
497
+ @staticmethod
498
+ def _merge_consecutive_audio_placeholders(
499
+ content: str,
500
+ audio_codes_list: List[torch.Tensor],
501
+ ) -> Tuple[str, List[torch.Tensor]]:
502
+ matches = list(re.finditer(re.escape(AUDIO_PLACEHOLDER), content))
503
+ if len(matches) <= 1:
504
+ return content, audio_codes_list
505
+
506
+ if len(matches) != len(audio_codes_list):
507
+ raise ValueError(
508
+ "Audio placeholders do not match the provided audio codes list."
509
+ )
510
+
511
+ new_audio_codes_list = []
512
+ new_parts = []
513
+ last_pos = 0
514
+ i = 0
515
+ while i < len(matches):
516
+ j = i
517
+ while (
518
+ j + 1 < len(matches)
519
+ and content[matches[j].end() : matches[j + 1].start()].strip() == ""
520
+ ):
521
+ j += 1
522
+
523
+ new_parts.append(content[last_pos : matches[i].start()])
524
+ new_parts.append(AUDIO_PLACEHOLDER)
525
+ last_pos = matches[j].end()
526
+
527
+ if j == i:
528
+ new_audio_codes_list.append(audio_codes_list[i])
529
+ else:
530
+ new_audio_codes_list.append(
531
+ torch.cat(audio_codes_list[i : j + 1], dim=0)
532
+ )
533
+
534
+ i = j + 1
535
+
536
+ new_parts.append(content[last_pos:])
537
+ return "".join(new_parts), new_audio_codes_list
538
+
539
+ @staticmethod
540
+ def apply_delay_pattern(codes: torch.Tensor, pad_code: int) -> torch.Tensor:
541
+ delayed_tokens = torch.full(
542
+ (codes.shape[0] + codes.shape[1] - 1, codes.shape[1]),
543
+ pad_code,
544
+ device=codes.device,
545
+ dtype=codes.dtype,
546
+ )
547
+ for i in range(codes.shape[1]):
548
+ delayed_tokens[i : i + codes.shape[0], i] = codes[:, i]
549
+ return delayed_tokens
550
+
551
+ @staticmethod
552
+ def apply_de_delay_pattern(delay_codes: torch.Tensor) -> torch.Tensor:
553
+ tokens = torch.full(
554
+ (delay_codes.shape[0] - delay_codes.shape[1] + 1, delay_codes.shape[1]),
555
+ 0,
556
+ device=delay_codes.device,
557
+ dtype=delay_codes.dtype,
558
+ )
559
+ for i in range(delay_codes.shape[1]):
560
+ tokens[:, i] = delay_codes[i : i + tokens.shape[0], i]
561
+ return tokens
562
+
563
+ def _get_unified_codes(
564
+ self,
565
+ role: str,
566
+ content: str,
567
+ audio_codes_list: List[torch.Tensor],
568
+ truncation: bool,
569
+ ) -> torch.Tensor:
570
+ """
571
+ 此时的 content 已经是带上了对话格式
572
+ """
573
+ if role == "user":
574
+ audio_gen_slot_token = audio_delay_slot_token = self.audio_user_slot_token
575
+ truncation = False
576
+ else:
577
+ audio_gen_slot_token = self.audio_assistant_gen_slot_token
578
+ audio_delay_slot_token = self.audio_assistant_delay_slot_token
579
+
580
+ if len(audio_codes_list):
581
+ n_vq = audio_codes_list[0].shape[1]
582
+ else:
583
+ n_vq = self.model_config.n_vq
584
+
585
+ if len(audio_codes_list) > 1 and AUDIO_PLACEHOLDER in content:
586
+ content, audio_codes_list = self._merge_consecutive_audio_placeholders(
587
+ content, audio_codes_list
588
+ )
589
+ content = self._replace_audio_placeholders(
590
+ content=content,
591
+ lengths=[len(audio_codes) for audio_codes in audio_codes_list],
592
+ n_vq=n_vq,
593
+ gen_slot_token=audio_gen_slot_token,
594
+ delay_slot_token=audio_delay_slot_token,
595
+ audio_start_token=self.audio_start_token,
596
+ audio_end_token=self.audio_end_token,
597
+ )
598
+ text_codes = torch.tensor(
599
+ self.tokenizer.encode(content),
600
+ device=audio_codes_list[0].device if audio_codes_list else None,
601
+ )
602
+
603
+ audio_start_indices = torch.where(
604
+ text_codes == self.model_config.audio_start_token_id
605
+ )[0]
606
+ audio_end_indices = torch.where(
607
+ text_codes == self.model_config.audio_end_token_id
608
+ )[0]
609
+ if len(audio_start_indices) != len(audio_codes_list) or len(
610
+ audio_end_indices
611
+ ) != len(audio_codes_list):
612
+ raise ValueError(
613
+ "Audio placeholders do not match the provided audio codes list."
614
+ )
615
+
616
+ delay_audio_codes_list = []
617
+ if len(audio_codes_list) == 0:
618
+ delay_audio_codes_list = torch.full(
619
+ (len(text_codes), n_vq),
620
+ self.model_config.audio_pad_code,
621
+ device=text_codes.device,
622
+ dtype=text_codes.dtype,
623
+ )
624
+ else:
625
+ prefix_idx = 0
626
+ for audio_start_idx_t, audio_end_idx_t, audio_codes in zip(
627
+ audio_start_indices, audio_end_indices, audio_codes_list
628
+ ):
629
+ audio_start_idx = int(audio_start_idx_t.item())
630
+ audio_end_idx = int(audio_end_idx_t.item())
631
+ delay_audio_codes = audio_codes # not delay
632
+ pad_codes = torch.full(
633
+ (audio_start_idx - prefix_idx + 1, n_vq),
634
+ self.model_config.audio_pad_code,
635
+ device=audio_codes.device,
636
+ dtype=audio_codes.dtype,
637
+ )
638
+ delay_audio_codes_list.extend([pad_codes, delay_audio_codes])
639
+ prefix_idx = audio_end_idx
640
+
641
+ if truncation:
642
+ ...
643
+ else:
644
+ last_audio_end_idx = int(audio_end_indices[-1].item())
645
+ pad_codes = torch.full(
646
+ (len(text_codes) - last_audio_end_idx, n_vq),
647
+ self.model_config.audio_pad_code,
648
+ device=audio_codes_list[0].device,
649
+ dtype=audio_codes_list[0].dtype,
650
+ )
651
+ delay_audio_codes_list.append(pad_codes)
652
+
653
+ delay_audio_codes_list = torch.cat(delay_audio_codes_list)
654
+
655
+ if text_codes.shape[0] != delay_audio_codes_list.shape[0]:
656
+ text_codes = text_codes[: delay_audio_codes_list.shape[0]]
657
+
658
+ unified_codes = torch.cat(
659
+ [text_codes.unsqueeze(1), delay_audio_codes_list], dim=1
660
+ )
661
+ return unified_codes
662
+
663
+ def _parse_text_codes(self, start_length, text_codes):
664
+ text = cast(str, self.tokenizer.decode(text_codes))
665
+ prefix = cast(str, self.tokenizer.decode(text_codes[:start_length]))
666
+ text = text[len(prefix) :]
667
+
668
+ AUDIO_PATTERN = re.compile(
669
+ rf"(?:{self.audio_start_token})?"
670
+ rf"(?:{self.audio_assistant_gen_slot_token})*"
671
+ rf"(?:{self.audio_assistant_delay_slot_token})*"
672
+ rf"{self.audio_end_token}"
673
+ )
674
+
675
+ def normalize_audio_segments(text: str) -> str:
676
+ def repl(match: re.Match) -> str:
677
+ seg = match.group(0)
678
+ # Replace with <|audio|> if gen_slot is present in the segment;
679
+ if self.audio_assistant_gen_slot_token in seg:
680
+ return AUDIO_PLACEHOLDER
681
+ # Otherwise, remove it.
682
+ return ""
683
+
684
+ return AUDIO_PATTERN.sub(repl, text)
685
+
686
+ return normalize_audio_segments(text)
687
+
688
+ def _parse_audio_codes(self, start_length, audio_codes):
689
+ # De-delay back to [T', n_vq]
690
+ # Rows that are all pad are separators between real audio segments.
691
+ is_pad = (audio_codes == self.model_config.audio_pad_code).all(dim=1)
692
+ non_pad = ~is_pad
693
+ if not non_pad.any():
694
+ return []
695
+
696
+ idx = torch.nonzero(non_pad).squeeze(1)
697
+ breaks = torch.where(idx[1:] != idx[:-1] + 1)[0] + 1
698
+ if breaks.numel() == 0:
699
+ segments_idx = [idx]
700
+ else:
701
+ segments_idx = torch.split(idx, breaks.tolist())
702
+
703
+ audio_codes_list = [audio_codes[s] for s in segments_idx]
704
+
705
+ # Batch-decode all audio segments together.
706
+ decoded_audio_list = self.decode_audio_codes(audio_codes_list)
707
+
708
+ # Keep codec causal context by decoding the whole first segment first,
709
+ # then trim at waveform level according to start_length ratio.
710
+ if (
711
+ start_length > 0
712
+ and len(audio_codes_list) > 0
713
+ and len(decoded_audio_list) > 0
714
+ ):
715
+ first_codes_length = audio_codes_list[0].shape[0]
716
+ if first_codes_length > 0:
717
+ trim_ratio = max(
718
+ 0.0, min(float(start_length) / float(first_codes_length), 1.0)
719
+ )
720
+ first_audio = decoded_audio_list[0]
721
+ if trim_ratio >= 1.0:
722
+ decoded_audio_list = decoded_audio_list[1:]
723
+ elif trim_ratio > 0.0:
724
+ trim_samples = int(first_audio.shape[-1] * trim_ratio)
725
+ decoded_audio_list[0] = first_audio[..., trim_samples:]
726
+
727
+ return decoded_audio_list
728
+
729
+ def decode(self, output: List[Tuple[int, torch.Tensor]]):
730
+ """
731
+ 1. 这里不管怎样,都需要一个完整的 assistant generation ids;
732
+ 2. 支持从任意位置进行截断;
733
+ """
734
+
735
+ genearted_messages = []
736
+ for start_length, generation_ids in output:
737
+ content = self._parse_text_codes(start_length, generation_ids[:, 0])
738
+ audio_codes_list = self._parse_audio_codes(
739
+ start_length, generation_ids[:, 1:]
740
+ )
741
+ if content == "":
742
+ message = None
743
+ else:
744
+ message = AssistantMessage(
745
+ content=content,
746
+ audio_codes_list=cast(
747
+ List[Union[str, torch.Tensor]], audio_codes_list
748
+ ),
749
+ )
750
+ genearted_messages.append(message)
751
+ return genearted_messages
752
+
753
+ @staticmethod
754
+ def loudness_normalize(
755
+ wav: torch.Tensor,
756
+ target_dbfs: float = -20,
757
+ gain_range: tuple[float, float] = (-3.0, 3.0),
758
+ ) -> torch.Tensor:
759
+ wav = wav.to(torch.float32)
760
+ if wav.numel() == 0:
761
+ return wav
762
+ current_dbfs = 10.0 * torch.log10(torch.mean(wav**2) + 1e-9)
763
+ gain = float(target_dbfs - current_dbfs)
764
+ gain = max(gain_range[0], min(gain, gain_range[1]))
765
+ factor = 10.0 ** (gain / 20.0)
766
+ return wav * factor
767
+
768
+ def _get_audio_tokenizer_device(self) -> torch.device:
769
+ """Best-effort device inference for `self.audio_tokenizer`.
770
+
771
+ Notes:
772
+ - Old TAC wrapper exposed `.device`, but standard `torch.nn.Module` does not.
773
+ - New MossAudioTokenizerModel is a `PreTrainedModel`; parameters define its device.
774
+ """
775
+
776
+ audio_tokenizer = getattr(self, "audio_tokenizer", None)
777
+ if audio_tokenizer is None:
778
+ logger.warning(
779
+ "audio_tokenizer is not set on processor. Using CPU as default."
780
+ )
781
+ return torch.device("cpu")
782
+
783
+ device_attr = getattr(audio_tokenizer, "device", None)
784
+ if isinstance(device_attr, torch.device):
785
+ return device_attr
786
+
787
+ try:
788
+ return next(audio_tokenizer.parameters()).device
789
+ except StopIteration:
790
+ # No parameters (shouldn't happen for real models); default to CPU.
791
+ logger.warning(
792
+ "No parameters found on audio_tokenizer. Using CPU as default."
793
+ )
794
+ return torch.device("cpu")
795
+
796
+ def encode_audios_from_wav(
797
+ self,
798
+ wav_list: List[torch.Tensor],
799
+ sampling_rate: int,
800
+ n_vq: Optional[int] = None,
801
+ ):
802
+ if self.audio_tokenizer is None:
803
+ raise RuntimeError("audio_tokenizer is not set on processor.")
804
+ audio_tokenizer = self.audio_tokenizer
805
+
806
+ if isinstance(wav_list, torch.Tensor):
807
+ wav_list = [wav_list]
808
+ wav_list_ = []
809
+ resample = False
810
+ if sampling_rate != self.model_config.sampling_rate:
811
+ resample = True
812
+ device = self._get_audio_tokenizer_device()
813
+ for wav in wav_list:
814
+ if wav.shape[0] > 1:
815
+ wav = torch.mean(wav, dim=0, keepdim=True)
816
+ if resample:
817
+ wav = torchaudio.functional.resample(
818
+ waveform=wav,
819
+ orig_freq=sampling_rate,
820
+ new_freq=self.model_config.sampling_rate,
821
+ )
822
+ wav = wav.to(device)
823
+ wav_list_.append(self.loudness_normalize(wav.squeeze(0)))
824
+
825
+ # New MossAudioTokenizerModel API: prefer batch_encode(list[wav])
826
+ if hasattr(audio_tokenizer, "batch_encode"):
827
+ enc = audio_tokenizer.batch_encode(wav_list_, num_quantizers=n_vq)
828
+ audio_codes = enc.audio_codes # (NQ, B, T)
829
+ audio_codes_lengths = enc.audio_codes_lengths # (B,)
830
+ else:
831
+ # Fallback: use encode() with explicit padding.
832
+ max_len = max(int(wav.shape[-1]) for wav in wav_list_)
833
+ input_values = torch.zeros(
834
+ len(wav_list_), 1, max_len, device=device, dtype=torch.float32
835
+ )
836
+ padding_mask = torch.zeros(
837
+ len(wav_list_), max_len, device=device, dtype=torch.bool
838
+ )
839
+ for i, wav in enumerate(wav_list_):
840
+ this_len = int(wav.shape[-1])
841
+ input_values[i, 0, :this_len] = wav
842
+ padding_mask[i, :this_len] = True
843
+ enc = audio_tokenizer.encode(
844
+ input_values,
845
+ padding_mask=padding_mask,
846
+ num_quantizers=n_vq,
847
+ return_dict=True,
848
+ )
849
+ audio_codes = enc.audio_codes
850
+ audio_codes_lengths = enc.audio_codes_lengths
851
+
852
+ if audio_codes is None or audio_codes_lengths is None:
853
+ raise RuntimeError(
854
+ "audio_tokenizer.encode() returned empty outputs (audio_codes/audio_codes_lengths)."
855
+ )
856
+
857
+ # Keep processor's historical contract: list[Tensor] with shape (T, NQ)
858
+ # and on CPU (so downstream text/audio packing remains device-agnostic).
859
+ codes_list: List[torch.Tensor] = []
860
+ for i in range(int(audio_codes.shape[1])):
861
+ length_i = int(audio_codes_lengths[i].item())
862
+ codes_i = (
863
+ audio_codes[:, i, :length_i]
864
+ .transpose(0, 1)
865
+ .contiguous()
866
+ .to(torch.long)
867
+ .cpu()
868
+ )
869
+ codes_list.append(codes_i)
870
+ return codes_list
871
+
872
+ def encode_audios_from_path(
873
+ self, wav_path_list: Union[str, List[str]], n_vq: Optional[int] = None
874
+ ):
875
+ if isinstance(wav_path_list, str):
876
+ wav_path_list = [wav_path_list]
877
+
878
+ if len(wav_path_list) == 0:
879
+ raise ValueError("Empty wav_path_list")
880
+
881
+ # Load + (if needed) resample each wav independently, so callers can
882
+ # pass a heterogeneous batch of files while still benefiting from
883
+ # audio_tokenizer.batch_encode.
884
+ target_sr = int(self.model_config.sampling_rate)
885
+ wav_list: List[torch.Tensor] = []
886
+ for wav_path in wav_path_list:
887
+ import soundfile as _sf # bypass torchcodec (Windows FFmpeg-shared issue)
888
+ _d, sr = _sf.read(str(wav_path), always_2d=True, dtype="float32")
889
+ wav = torch.from_numpy(_d.T).contiguous() # [channels, frames]
890
+ if int(sr) != target_sr:
891
+ wav = torchaudio.functional.resample(
892
+ waveform=wav,
893
+ orig_freq=int(sr),
894
+ new_freq=target_sr,
895
+ )
896
+ wav_list.append(wav)
897
+
898
+ return self.encode_audios_from_wav(wav_list, target_sr, n_vq)
899
+
900
+ def decode_audio_codes(
901
+ self, audio_tokens_list: Union[torch.Tensor, List[torch.Tensor]]
902
+ ):
903
+ if self.audio_tokenizer is None:
904
+ raise RuntimeError("audio_tokenizer is not set on processor.")
905
+ audio_tokenizer = self.audio_tokenizer
906
+
907
+ if isinstance(audio_tokens_list, torch.Tensor):
908
+ audio_tokens_list = [audio_tokens_list]
909
+ if len(audio_tokens_list) == 0:
910
+ return []
911
+
912
+ device = self._get_audio_tokenizer_device()
913
+
914
+ # Processor uses (T, NQ); MossAudioTokenizer expects (NQ, T) (or (NQ, B, T)).
915
+ codes_list = [
916
+ codes.transpose(0, 1).contiguous().to(device=device, dtype=torch.long)
917
+ for codes in audio_tokens_list
918
+ ]
919
+
920
+ # Fallback: pad to (NQ, B, T) + mask, then decode.
921
+ nq = int(codes_list[0].shape[0])
922
+ max_t = max(int(c.shape[1]) for c in codes_list)
923
+ audio_codes = torch.zeros(
924
+ nq, len(codes_list), max_t, device=device, dtype=torch.long
925
+ )
926
+ padding_mask = torch.zeros(
927
+ len(codes_list), max_t, device=device, dtype=torch.bool
928
+ )
929
+ for i, c in enumerate(codes_list):
930
+ t = int(c.shape[1])
931
+ audio_codes[:, i, :t] = c
932
+ padding_mask[i, :t] = True
933
+ dec = audio_tokenizer.decode(
934
+ audio_codes, padding_mask=padding_mask, return_dict=True, chunk_duration=8
935
+ )
936
+ audio = dec.audio
937
+ audio_lengths = dec.audio_lengths
938
+
939
+ if audio is None or audio_lengths is None:
940
+ raise RuntimeError(
941
+ "audio_tokenizer.decode() returned empty outputs (audio/audio_lengths)."
942
+ )
943
+
944
+ # Return historical contract: list of 1D waveforms (T,)
945
+ wav_list: List[torch.Tensor] = []
946
+ for i in range(int(audio.shape[0])):
947
+ length_i = int(audio_lengths[i].item())
948
+ wav = audio[i, 0, :length_i].contiguous().to(torch.float32).cpu()
949
+ wav_list.append(wav)
950
+ return wav_list
processor_config.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "processor_class": "MossTTSDelayProcessor",
3
+ "auto_map": {
4
+ "AutoProcessor": "processing_moss_tts.MossTTSDelayProcessor"
5
+ },
6
+ "audio_tokenizer_name_or_path": "OpenMOSS-Team/MOSS-Audio-Tokenizer"
7
+ }
special_tokens_map.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<|im_start|>",
4
+ "<|im_end|>",
5
+ "<|object_ref_start|>",
6
+ "<|object_ref_end|>",
7
+ "<|box_start|>",
8
+ "<|box_end|>",
9
+ "<|quad_start|>",
10
+ "<|quad_end|>",
11
+ "<|audio_start|>",
12
+ "<|audio_end|>",
13
+ "<|audio_user_slot|>",
14
+ "<|image_pad|>",
15
+ "<|audio_assistant_gen_slot|>"
16
+ ],
17
+ "eos_token": {
18
+ "content": "<|im_end|>",
19
+ "lstrip": false,
20
+ "normalized": false,
21
+ "rstrip": false,
22
+ "single_word": false
23
+ },
24
+ "pad_token": {
25
+ "content": "<|endoftext|>",
26
+ "lstrip": false,
27
+ "normalized": false,
28
+ "rstrip": false,
29
+ "single_word": false
30
+ }
31
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cb3c8fa82993d515469c2800cc455bff4aaa3c4fed9da1f2b0c0668c304f335a
3
+ size 11422691
tokenizer_config.json ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_prefix_space": false,
4
+ "added_tokens_decoder": {
5
+ "151643": {
6
+ "content": "<|endoftext|>",
7
+ "lstrip": false,
8
+ "normalized": false,
9
+ "rstrip": false,
10
+ "single_word": false,
11
+ "special": true
12
+ },
13
+ "151644": {
14
+ "content": "<|im_start|>",
15
+ "lstrip": false,
16
+ "normalized": false,
17
+ "rstrip": false,
18
+ "single_word": false,
19
+ "special": true
20
+ },
21
+ "151645": {
22
+ "content": "<|im_end|>",
23
+ "lstrip": false,
24
+ "normalized": false,
25
+ "rstrip": false,
26
+ "single_word": false,
27
+ "special": true
28
+ },
29
+ "151646": {
30
+ "content": "<|object_ref_start|>",
31
+ "lstrip": false,
32
+ "normalized": false,
33
+ "rstrip": false,
34
+ "single_word": false,
35
+ "special": true
36
+ },
37
+ "151647": {
38
+ "content": "<|object_ref_end|>",
39
+ "lstrip": false,
40
+ "normalized": false,
41
+ "rstrip": false,
42
+ "single_word": false,
43
+ "special": true
44
+ },
45
+ "151648": {
46
+ "content": "<|box_start|>",
47
+ "lstrip": false,
48
+ "normalized": false,
49
+ "rstrip": false,
50
+ "single_word": false,
51
+ "special": true
52
+ },
53
+ "151649": {
54
+ "content": "<|box_end|>",
55
+ "lstrip": false,
56
+ "normalized": false,
57
+ "rstrip": false,
58
+ "single_word": false,
59
+ "special": true
60
+ },
61
+ "151650": {
62
+ "content": "<|quad_start|>",
63
+ "lstrip": false,
64
+ "normalized": false,
65
+ "rstrip": false,
66
+ "single_word": false,
67
+ "special": true
68
+ },
69
+ "151651": {
70
+ "content": "<|quad_end|>",
71
+ "lstrip": false,
72
+ "normalized": false,
73
+ "rstrip": false,
74
+ "single_word": false,
75
+ "special": true
76
+ },
77
+ "151652": {
78
+ "content": "<|audio_start|>",
79
+ "lstrip": false,
80
+ "normalized": false,
81
+ "rstrip": false,
82
+ "single_word": false,
83
+ "special": true
84
+ },
85
+ "151653": {
86
+ "content": "<|audio_end|>",
87
+ "lstrip": false,
88
+ "normalized": false,
89
+ "rstrip": false,
90
+ "single_word": false,
91
+ "special": true
92
+ },
93
+ "151654": {
94
+ "content": "<|audio_user_slot|>",
95
+ "lstrip": false,
96
+ "normalized": false,
97
+ "rstrip": false,
98
+ "single_word": false,
99
+ "special": true
100
+ },
101
+ "151655": {
102
+ "content": "<|image_pad|>",
103
+ "lstrip": false,
104
+ "normalized": false,
105
+ "rstrip": false,
106
+ "single_word": false,
107
+ "special": true
108
+ },
109
+ "151656": {
110
+ "content": "<|audio_assistant_gen_slot|>",
111
+ "lstrip": false,
112
+ "normalized": false,
113
+ "rstrip": false,
114
+ "single_word": false,
115
+ "special": true
116
+ },
117
+ "151657": {
118
+ "content": "<tool_call>",
119
+ "lstrip": false,
120
+ "normalized": false,
121
+ "rstrip": false,
122
+ "single_word": false,
123
+ "special": false
124
+ },
125
+ "151658": {
126
+ "content": "</tool_call>",
127
+ "lstrip": false,
128
+ "normalized": false,
129
+ "rstrip": false,
130
+ "single_word": false,
131
+ "special": false
132
+ },
133
+ "151659": {
134
+ "content": "<|fim_prefix|>",
135
+ "lstrip": false,
136
+ "normalized": false,
137
+ "rstrip": false,
138
+ "single_word": false,
139
+ "special": false
140
+ },
141
+ "151660": {
142
+ "content": "<|fim_middle|>",
143
+ "lstrip": false,
144
+ "normalized": false,
145
+ "rstrip": false,
146
+ "single_word": false,
147
+ "special": false
148
+ },
149
+ "151661": {
150
+ "content": "<|fim_suffix|>",
151
+ "lstrip": false,
152
+ "normalized": false,
153
+ "rstrip": false,
154
+ "single_word": false,
155
+ "special": false
156
+ },
157
+ "151662": {
158
+ "content": "<|audio_assistant_delay_slot|>",
159
+ "lstrip": false,
160
+ "normalized": false,
161
+ "rstrip": false,
162
+ "single_word": false,
163
+ "special": false
164
+ },
165
+ "151663": {
166
+ "content": "<|repo_name|>",
167
+ "lstrip": false,
168
+ "normalized": false,
169
+ "rstrip": false,
170
+ "single_word": false,
171
+ "special": false
172
+ },
173
+ "151664": {
174
+ "content": "<|file_sep|>",
175
+ "lstrip": false,
176
+ "normalized": false,
177
+ "rstrip": false,
178
+ "single_word": false,
179
+ "special": false
180
+ },
181
+ "151665": {
182
+ "content": "<tool_response>",
183
+ "lstrip": false,
184
+ "normalized": false,
185
+ "rstrip": false,
186
+ "single_word": false,
187
+ "special": false
188
+ },
189
+ "151666": {
190
+ "content": "</tool_response>",
191
+ "lstrip": false,
192
+ "normalized": false,
193
+ "rstrip": false,
194
+ "single_word": false,
195
+ "special": false
196
+ },
197
+ "151667": {
198
+ "content": "<think>",
199
+ "lstrip": false,
200
+ "normalized": false,
201
+ "rstrip": false,
202
+ "single_word": false,
203
+ "special": false
204
+ },
205
+ "151668": {
206
+ "content": "</think>",
207
+ "lstrip": false,
208
+ "normalized": false,
209
+ "rstrip": false,
210
+ "single_word": false,
211
+ "special": false
212
+ }
213
+ },
214
+ "additional_special_tokens": [
215
+ "<|im_start|>",
216
+ "<|im_end|>",
217
+ "<|object_ref_start|>",
218
+ "<|object_ref_end|>",
219
+ "<|box_start|>",
220
+ "<|box_end|>",
221
+ "<|quad_start|>",
222
+ "<|quad_end|>",
223
+ "<|audio_start|>",
224
+ "<|audio_end|>",
225
+ "<|audio_user_slot|>",
226
+ "<|image_pad|>",
227
+ "<|audio_assistant_gen_slot|>"
228
+ ],
229
+ "bos_token": null,
230
+ "clean_up_tokenization_spaces": false,
231
+ "eos_token": "<|im_end|>",
232
+ "errors": "replace",
233
+ "extra_special_tokens": {},
234
+ "model_max_length": 131072,
235
+ "pad_token": "<|endoftext|>",
236
+ "processor_class": "AsteroidProcessor",
237
+ "split_special_tokens": false,
238
+ "tokenizer_class": "Qwen2Tokenizer",
239
+ "unk_token": null
240
+ }
vocab.json ADDED
The diff for this file is too large to render. See raw diff