""" models.py -- loads and runs the three ASR conditions under blind test: BASELINE (zero-shot pretrained), VANILLA_FT, and DECLARE. *** YOU MUST SUPPLY YOUR OWN CHECKPOINTS *** This sandbox has no access to your NeMo checkpoints, NGC, or a GPU, so the model-loading code below is written against the standard NeMo API used throughout your existing notebooks (`ASRModel.from_pretrained` / `ASRModel.restore_from`) but has not been executed end-to-end. Before deploying, do one of: 1. Upload your three .nemo files into this Space's repo (e.g. under checkpoints/) and point CHECKPOINT_PATHS at them, or 2. Push each checkpoint to its own private HF model repo and set the corresponding entry in CHECKPOINT_PATHS to that repo id, or 3. Point BASELINE at the public NGC pretrained model name (as in your other notebooks) since that one needs no fine-tuned weights at all. If a checkpoint is missing, that condition falls back to a clearly-labelled MOCK transcription so the rest of the app (routing, NLU, logging) can still be tested and demoed without real model weights. """ import os import random MODEL_ID = os.environ.get("BASELINE_MODEL_ID", "stt_en_conformer_ctc_small_ls") # Fill these in with real paths / HF repo ids before deploying. CHECKPOINT_PATHS = { "BASELINE": None, # loaded via from_pretrained(MODEL_ID) below -- no path needed "VANILLA_FT": os.environ.get("VANILLA_FT_CKPT", "checkpoints/vanilla_ft.nemo"), "DECLARE": os.environ.get("DECLARE_CKPT", "checkpoints/declare.nemo"), } _loaded_models = {} _nemo_available = False try: import nemo.collections.asr as nemo_asr # noqa: F401 _nemo_available = True except Exception: _nemo_available = False def _load_model(condition: str): """Lazily loads and caches one of the three ASR models.""" if condition in _loaded_models: return _loaded_models[condition] if not _nemo_available: _loaded_models[condition] = None return None import nemo.collections.asr as nemo_asr try: if condition == "BASELINE": model = nemo_asr.models.ASRModel.from_pretrained(model_name=MODEL_ID) else: ckpt = CHECKPOINT_PATHS[condition] if not ckpt or not os.path.exists(ckpt): print(f"[models.py] Checkpoint not found for {condition}: {ckpt}") _loaded_models[condition] = None return None model = nemo_asr.models.ASRModel.restore_from(ckpt) model.eval() _loaded_models[condition] = model return model except Exception as e: print(f"[models.py] Failed to load {condition}: {e}") _loaded_models[condition] = None return None _MOCK_NOISE = [ lambda s: s, # occasionally perfect lambda s: s.replace("the", "da").replace("to", "too"), lambda s: " ".join(w[::-1] if len(w) > 4 else w for w in s.split()[:3]) + " " + " ".join(s.split()[3:]), ] def _mock_transcribe(reference_text: str, condition: str) -> str: """Used only when no real checkpoint/NeMo install is available, so the rest of the pipeline (routing, NLU, logging, UI) remains demoable. Clearly NOT a substitute for real model inference.""" random.seed(hash((reference_text, condition)) % (2**32)) noiser = random.choice(_MOCK_NOISE) return f"[MOCK-{condition}] " + noiser(reference_text) def transcribe(condition: str, audio_path: str, reference_text: str = "") -> str: """Runs ASR for the given blind condition ('BASELINE' | 'VANILLA_FT' | 'DECLARE'). Falls back to a labelled mock transcription if no real model is loaded, so the app remains runnable before checkpoints are supplied. The audio file at `audio_path` is deleted immediately after this function returns (see the `finally` block) -- only the resulting transcript is ever logged or retained. This is a hard participant-facing commitment, not just a UI statement, so it is enforced here rather than left to the caller. """ model = _load_model(condition) try: if model is None: return _mock_transcribe(reference_text, condition) try: out = model.transcribe([audio_path], verbose=False)[0] return out.text if hasattr(out, "text") else out except Exception as e: print(f"[models.py] Inference failed for {condition}: {e}") return _mock_transcribe(reference_text, condition) finally: try: if audio_path and os.path.exists(audio_path): os.remove(audio_path) except Exception as e: print(f"[models.py] Warning: could not delete audio file {audio_path}: {e}") def status_report() -> dict: """For an admin/debug panel: which conditions have real models loaded.""" return { "nemo_available": _nemo_available, "baseline_loaded": _load_model("BASELINE") is not None, "vanilla_ft_loaded": _load_model("VANILLA_FT") is not None, "declare_loaded": _load_model("DECLARE") is not None, }