1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5pub enum PositionalEmbedding {
6 Rope,
7 Sin,
8 None,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TransformerConfig {
14 pub d_model: usize,
15 pub num_heads: usize,
16 pub num_layers: usize,
17 pub dim_feedforward: usize,
18 pub causal: bool,
19 pub norm_first: bool,
20 pub context: usize,
21 pub max_period: usize,
22 pub positional_embedding: PositionalEmbedding,
23 pub kv_repeat: usize,
24}
25
26impl TransformerConfig {
27 pub fn swiglu_hidden(&self) -> usize {
29 if self.dim_feedforward == 4 * self.d_model {
30 11 * self.d_model / 4
31 } else {
32 2 * self.dim_feedforward / 3
33 }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct DepFormerConfig {
40 pub transformer: TransformerConfig,
41 pub num_slices: usize,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct LmConfig {
47 pub transformer: TransformerConfig,
48 pub depformer: Option<DepFormerConfig>,
49 pub text_in_vocab_size: usize,
50 pub text_out_vocab_size: usize,
51 pub audio_vocab_size: usize,
52 pub audio_codebooks: usize,
53}
54
55impl LmConfig {
56 fn depformer_cfg(num_slices: usize) -> DepFormerConfig {
57 DepFormerConfig {
58 num_slices,
59 transformer: TransformerConfig {
60 d_model: 1024,
61 num_heads: 16,
62 num_layers: 6,
63 dim_feedforward: 1024 * 4,
64 causal: true,
65 norm_first: true,
66 context: num_slices,
67 max_period: 10_000,
68 positional_embedding: PositionalEmbedding::None,
69 kv_repeat: 1,
70 },
71 }
72 }
73
74 pub fn v0_1() -> Self {
76 Self {
77 transformer: TransformerConfig {
78 d_model: 4096,
79 num_heads: 32,
80 num_layers: 32,
81 dim_feedforward: 4096 * 4,
82 causal: true,
83 norm_first: true,
84 context: 3000,
85 max_period: 10_000,
86 positional_embedding: PositionalEmbedding::Rope,
87 kv_repeat: 1,
88 },
89 depformer: Some(Self::depformer_cfg(8)),
90 audio_vocab_size: 2049,
91 text_in_vocab_size: 32_001,
92 text_out_vocab_size: 32_000,
93 audio_codebooks: 8,
94 }
95 }
96
97 pub fn v0_1_streaming(num_slices: usize) -> Self {
99 let mut s = Self::v0_1();
100 s.audio_codebooks = 16;
101 if let Some(dep) = s.depformer.as_mut() {
102 dep.num_slices = num_slices;
103 dep.transformer.context = num_slices;
104 }
105 s
106 }
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct GenerateConfig {
112 pub generated_audio_codebooks: usize,
113 pub input_audio_codebooks: usize,
114 pub audio_vocab_size: usize,
115 pub acoustic_delay: usize,
116 pub text_pad_token: u32,
117 pub text_eop_token: u32,
118 pub text_start_token: u32,
119}
120
121impl GenerateConfig {
122 pub fn v0_1() -> Self {
123 Self {
124 generated_audio_codebooks: 8,
125 input_audio_codebooks: 8,
126 audio_vocab_size: 2049,
127 acoustic_delay: 2,
128 text_eop_token: 0,
129 text_pad_token: 3,
130 text_start_token: 32_000,
131 }
132 }
133
134 pub fn v0_1_one_way() -> Self {
136 Self {
137 generated_audio_codebooks: 8,
138 input_audio_codebooks: 0,
139 ..Self::v0_1()
140 }
141 }
142
143 pub fn audio_pad_token(&self) -> u32 {
144 self.audio_vocab_size as u32 - 1
145 }
146
147 pub fn total_audio_codebooks(&self) -> usize {
148 self.generated_audio_codebooks + self.input_audio_codebooks
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
154pub enum MoshiVoice {
155 Moshiko,
157 Moshika,
159}
160
161impl MoshiVoice {
162 pub fn parse(name: &str) -> Option<Self> {
164 match name.to_ascii_lowercase().as_str() {
165 "moshiko" | "male" => Some(Self::Moshiko),
166 "moshika" | "female" => Some(Self::Moshika),
167 _ => None,
168 }
169 }
170
171 pub fn from_env_or_default() -> Self {
173 std::env::var("RLX_MOSHI_VOICE")
174 .ok()
175 .and_then(|s| Self::parse(&s))
176 .unwrap_or(Self::Moshiko)
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182pub enum MoshiVariant {
183 Moshiko,
185 MoshikoOneWay,
187 Moshika,
189 MoshikaOneWay,
191}
192
193impl MoshiVariant {
194 pub fn voice(self) -> MoshiVoice {
196 match self {
197 Self::Moshiko | Self::MoshikoOneWay => MoshiVoice::Moshiko,
198 Self::Moshika | Self::MoshikaOneWay => MoshiVoice::Moshika,
199 }
200 }
201
202 pub fn is_one_way(self) -> bool {
204 matches!(self, Self::MoshikoOneWay | Self::MoshikaOneWay)
205 }
206
207 pub fn is_duplex(self) -> bool {
209 matches!(self, Self::Moshiko | Self::Moshika)
210 }
211
212 pub fn parse(name: &str) -> Option<Self> {
214 match name.to_ascii_lowercase().as_str() {
215 "moshiko-one-way" | "moshiko-oneway" | "oneway" => Some(Self::MoshikoOneWay),
216 "moshiko" | "duplex" => Some(Self::Moshiko),
217 "moshika-one-way" | "moshika-oneway" => Some(Self::MoshikaOneWay),
218 "moshika" => Some(Self::Moshika),
219 _ => None,
220 }
221 }
222
223 pub fn hf_repo(self, checkpoint: super::checkpoint::MoshiCheckpoint) -> &'static str {
225 checkpoint.hf_repo(self.voice())
226 }
227
228 pub fn lm_config(self) -> LmConfig {
230 match self {
231 Self::Moshiko | Self::Moshika => LmConfig::v0_1_streaming(8),
232 Self::MoshikoOneWay | Self::MoshikaOneWay => LmConfig::v0_1(),
233 }
234 }
235
236 pub fn generate_config(self) -> GenerateConfig {
238 match self {
239 Self::Moshiko | Self::Moshika => GenerateConfig::v0_1(),
240 Self::MoshikoOneWay | Self::MoshikaOneWay => GenerateConfig::v0_1_one_way(),
241 }
242 }
243}