Spaces:
Running on Zero
Running on Zero
Download app.py from codewithmoin/smart-turn-hinglish: direct link, hf CLI and curl.
- Browser
- Download file 2.59 kB
-
https://huggingface.co/spaces/codewithmoin/smart-turn-hinglish/resolve/main/app.py
- Command line
-
hf download hf://spaces/codewithmoin/smart-turn-hinglish/app.py
-
curl -L -o app.py https://huggingface.co/spaces/codewithmoin/smart-turn-hinglish/resolve/main/app.py
2.59 kB
| """Smart Turn Detection — Hugging Face Space demo (ONNX, CPU). | |
| Runs the exported 8s model: raw 16k waveform -> P(turn complete). | |
| Inference is CPU-only, so predictions don't touch the GPU queue. The one decorated | |
| `_gpu_probe` below exists only so the ZeroGPU runtime accepts the Space; it's never called.""" | |
| import numpy as np | |
| import onnxruntime as ort | |
| import gradio as gr | |
| try: | |
| import spaces | |
| def _gpu_probe(): # satisfies ZeroGPU's "must expose a GPU function" check; unused | |
| return None | |
| except Exception: | |
| pass | |
| SR, WIN = 16000, 8 | |
| N = SR * WIN # 128000 | |
| SESS = ort.InferenceSession("smart_turn_8s.onnx", providers=["CPUExecutionProvider"]) | |
| def prob_complete(wav, sr): | |
| wav = np.asarray(wav, dtype=np.float32) | |
| if wav.ndim > 1: | |
| wav = wav.mean(axis=1) | |
| if sr != SR: | |
| import librosa | |
| wav = librosa.resample(wav, orig_sr=sr, target_sr=SR) | |
| mx = np.abs(wav).max() | |
| if mx > 0: | |
| wav = wav / mx | |
| x = wav[-N:] # last 8s | |
| buf = np.zeros((1, N), np.float32) | |
| buf[0, N - len(x):] = x # right-align (left-pad) | |
| return float(SESS.run(None, {"waveform": buf})[0][0]) | |
| def predict(audio, threshold): | |
| if audio is None: | |
| return "Record or upload some speech.", None | |
| sr, wav = audio | |
| if wav.dtype.kind in "iu": | |
| wav = wav.astype(np.float32) / np.iinfo(wav.dtype).max | |
| p = prob_complete(wav, sr) | |
| verdict = "✅ Turn COMPLETE — safe to respond" if p > threshold else "⏳ Still speaking — wait" | |
| return f"{verdict}\n\nP(complete) = {p:.3f} (threshold {threshold:.2f})", {"complete": p, "still speaking": 1 - p} | |
| with gr.Blocks(title="Smart Turn Detection") as demo: | |
| gr.Markdown( | |
| "# 🎙️ Smart Turn Detection (whisper-tiny, 8s)\n" | |
| "Has the speaker **finished their turn**, or are they just pausing? Audio only, " | |
| "no transcription. Tuned for English + Hindi/Hinglish. ~7.8M params, ~38 ms/clip on CPU.\n\n" | |
| "_Try a finished sentence vs. trailing off with 'umm…'. Raise the threshold to interrupt less._") | |
| inp = gr.Audio(sources=["microphone", "upload"], type="numpy", label="Speech") | |
| thr = gr.Slider(0.05, 0.95, value=0.5, step=0.05, label="Decision threshold") | |
| btn = gr.Button("Detect turn", variant="primary") | |
| out_txt = gr.Textbox(label="Verdict") | |
| out_lbl = gr.Label(label="Probabilities") | |
| btn.click(predict, [inp, thr], [out_txt, out_lbl]) | |
| inp.stop_recording(predict, [inp, thr], [out_txt, out_lbl]) | |
| if __name__ == "__main__": | |
| demo.launch() | |