| 212 | } |
| 213 | |
| 214 | int rcli_init(RCLIHandle handle, const char* models_dir, int gpu_layers) { |
| 215 | if (!handle || !models_dir) return -1; |
| 216 | auto* engine = static_cast<RCLIEngine*>(handle); |
| 217 | |
| 218 | std::string dir(models_dir); |
| 219 | engine->models_dir = dir; |
| 220 | |
| 221 | PipelineConfig config; |
| 222 | |
| 223 | // --- STT (Zipformer streaming — always active for live mic) --- |
| 224 | config.stt.encoder_path = dir + "/zipformer/encoder-epoch-99-avg-1.int8.onnx"; |
| 225 | config.stt.decoder_path = dir + "/zipformer/decoder-epoch-99-avg-1.int8.onnx"; |
| 226 | config.stt.joiner_path = dir + "/zipformer/joiner-epoch-99-avg-1.int8.onnx"; |
| 227 | config.stt.tokens_path = dir + "/zipformer/tokens.txt"; |
| 228 | config.stt.sample_rate = 16000; |
| 229 | config.stt.num_threads = 2; |
| 230 | |
| 231 | // --- Offline STT (resolve: user preference > auto-detect highest priority) --- |
| 232 | { |
| 233 | auto stt_models = rcli::all_stt_models(); |
| 234 | const auto* active_stt = rcli::resolve_active_stt(dir, stt_models); |
| 235 | if (!active_stt) active_stt = rcli::get_default_offline_stt(stt_models); |
| 236 | |
| 237 | if (active_stt && active_stt->backend == "nemo_transducer") { |
| 238 | std::string base = dir + "/" + active_stt->dir_name; |
| 239 | config.offline_stt.backend = OfflineSttBackend::NEMO_TRANSDUCER; |
| 240 | config.offline_stt.transducer_encoder_path = base + "/" + active_stt->encoder_file; |
| 241 | config.offline_stt.transducer_decoder_path = base + "/" + active_stt->decoder_file; |
| 242 | config.offline_stt.transducer_joiner_path = base + "/" + active_stt->joiner_file; |
| 243 | config.offline_stt.tokens_path = base + "/" + active_stt->tokens_file; |
| 244 | config.offline_stt.sample_rate = 16000; |
| 245 | config.offline_stt.num_threads = 4; |
| 246 | engine->using_parakeet = true; |
| 247 | engine->stt_model_name = active_stt->name; |
| 248 | LOG_DEBUG("RCLI", "Using %s for offline STT", active_stt->name.c_str()); |
| 249 | } else if (active_stt) { |
| 250 | std::string base = dir + "/" + active_stt->dir_name; |
| 251 | config.offline_stt.backend = OfflineSttBackend::WHISPER; |
| 252 | config.offline_stt.encoder_path = base + "/" + active_stt->encoder_file; |
| 253 | config.offline_stt.decoder_path = base + "/" + active_stt->decoder_file; |
| 254 | config.offline_stt.tokens_path = base + "/" + active_stt->tokens_file; |
| 255 | config.offline_stt.language = "en"; |
| 256 | config.offline_stt.task = "transcribe"; |
| 257 | config.offline_stt.tail_paddings = 500; |
| 258 | config.offline_stt.sample_rate = 16000; |
| 259 | config.offline_stt.num_threads = 4; |
| 260 | engine->using_parakeet = false; |
| 261 | engine->stt_model_name = active_stt->name; |
| 262 | LOG_DEBUG("RCLI", "Using %s for offline STT", active_stt->name.c_str()); |
| 263 | } else { |
| 264 | config.offline_stt.backend = OfflineSttBackend::WHISPER; |
| 265 | config.offline_stt.encoder_path = dir + "/whisper-base.en/base.en-encoder.int8.onnx"; |
| 266 | config.offline_stt.decoder_path = dir + "/whisper-base.en/base.en-decoder.int8.onnx"; |
| 267 | config.offline_stt.tokens_path = dir + "/whisper-base.en/base.en-tokens.txt"; |
| 268 | config.offline_stt.language = "en"; |
| 269 | config.offline_stt.task = "transcribe"; |
| 270 | config.offline_stt.tail_paddings = 500; |
| 271 | config.offline_stt.sample_rate = 16000; |