1use crate::checkpoint::MoshiCheckpoint;
4use crate::config::{GenerateConfig, MoshiVariant};
5use crate::generate::GenerateState;
6use crate::lm::LmModel;
7use crate::rlx_gen::{RlxGenerateState, RlxLm};
8use crate::sampling::LogitsProcessor;
9use crate::weights::{open_lm, open_lm_from_weights};
10use anyhow::{Result, ensure};
11use rlx_runtime::Device;
12use std::path::Path;
13
14pub enum MoshiLm {
16 Eager(LmModel),
18 Rlx(RlxLm),
20}
21
22pub enum MoshiGenState {
24 Eager(GenerateState),
25 Rlx(RlxGenerateState),
26}
27
28impl MoshiLm {
29 pub fn open(
30 model_dir: &Path,
31 variant: MoshiVariant,
32 checkpoint: MoshiCheckpoint,
33 device: Device,
34 ) -> Result<Self> {
35 if std::env::var_os("RLX_MOSHI_NATIVE").is_some() || device != Device::Cpu {
37 return Ok(Self::Rlx(RlxLm::open(
38 model_dir, variant, checkpoint, device,
39 )?));
40 }
41
42 let cfg = variant.lm_config();
44 let weights_path = checkpoint.lm_weights_path(model_dir);
45 if checkpoint.is_mlx() {
46 let weights =
47 crate::mlx_weights::load_eager_weight_map(&weights_path, checkpoint, &cfg)?;
48 return Ok(Self::Eager(open_lm_from_weights(cfg, weights)?));
49 }
50 let lm = if checkpoint.is_gguf() {
51 let weights = crate::gguf::load_gguf_weight_map(&weights_path, &cfg)?;
52 open_lm_from_weights(cfg, weights)?
53 } else {
54 open_lm(model_dir, cfg)?
55 };
56 Ok(Self::Eager(lm))
57 }
58
59 pub fn config(&self) -> &crate::config::LmConfig {
60 match self {
61 Self::Eager(m) => m.config(),
62 Self::Rlx(m) => m.config(),
63 }
64 }
65
66 pub fn reset_state(&mut self) {
67 match self {
68 Self::Eager(m) => m.reset_state(),
69 Self::Rlx(_) => {}
70 }
71 }
72
73 pub fn new_gen_state(
74 &self,
75 max_steps: usize,
76 text_lp: LogitsProcessor,
77 audio_lp: LogitsProcessor,
78 gen_cfg: GenerateConfig,
79 ) -> Result<MoshiGenState> {
80 match self {
81 Self::Eager(_) => Ok(MoshiGenState::Eager(GenerateState::new(
82 max_steps, text_lp, audio_lp, gen_cfg,
83 ))),
84 Self::Rlx(_) => Ok(MoshiGenState::Rlx(RlxGenerateState::new(
85 max_steps, text_lp, audio_lp, gen_cfg,
86 ))),
87 }
88 }
89}
90
91impl MoshiGenState {
92 pub fn config(&self) -> &GenerateConfig {
93 match self {
94 Self::Eager(s) => s.config(),
95 Self::Rlx(s) => s.config(),
96 }
97 }
98
99 pub fn step_idx(&self) -> usize {
100 match self {
101 Self::Eager(s) => s.step_idx(),
102 Self::Rlx(s) => s.step_idx(),
103 }
104 }
105
106 pub fn text_tokens(&self) -> &[u32] {
107 match self {
108 Self::Eager(s) => s.text_tokens(),
109 Self::Rlx(s) => s.text_tokens(),
110 }
111 }
112
113 pub fn step(&mut self, lm: &mut MoshiLm, text_token: u32, input_audio: &[u32]) -> Result<u32> {
114 match (self, lm) {
115 (Self::Eager(s), MoshiLm::Eager(m)) => s.step(m, text_token, input_audio),
116 (Self::Rlx(s), MoshiLm::Rlx(m)) => s.step(m, text_token, input_audio),
117 #[allow(unreachable_patterns)]
118 _ => {
119 ensure!(false, "Moshi LM / generation backend mismatch");
120 Ok(0)
121 }
122 }
123 }
124
125 pub fn last_audio_frame(&self) -> Option<Vec<u32>> {
126 match self {
127 Self::Eager(s) => s.last_audio_frame(),
128 Self::Rlx(s) => s.last_audio_frame(),
129 }
130 }
131
132 pub fn reset(
133 &mut self,
134 lm: &mut MoshiLm,
135 max_steps: usize,
136 text_lp: LogitsProcessor,
137 audio_lp: LogitsProcessor,
138 ) -> Result<()> {
139 let _ = (max_steps, text_lp, audio_lp);
140 match (self, lm) {
141 (Self::Eager(s), MoshiLm::Eager(m)) => {
142 s.reset(m);
143 Ok(())
144 }
145 (Self::Rlx(s), MoshiLm::Rlx(_m)) => {
146 s.reset();
148 Ok(())
149 }
150 #[allow(unreachable_patterns)]
151 _ => anyhow::bail!("Moshi LM / generation backend mismatch"),
152 }
153 }
154}
155
156pub fn resolve_lm_device(requested: Device, _checkpoint: MoshiCheckpoint) -> Device {
158 if requested == Device::Cpu {
159 return Device::Cpu;
160 }
161 if rlx_runtime::is_available(requested) {
162 requested
163 } else {
164 Device::Cpu
165 }
166}