File size: 3,480 Bytes
c99c33c
 
 
 
 
 
 
 
3db1df6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c99c33c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ebd33ab
85967d5
 
 
ebd33ab
c99c33c
 
 
 
 
 
 
 
3db1df6
c99c33c
 
fee9391
c99c33c
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python3
"""Gradio Space for MiMo-V2.5-ASR MLX."""

from __future__ import annotations

import os
import time
from functools import lru_cache
from pathlib import Path

os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "1")


def _install_gxx_wrapper() -> None:
    """Make MLX CPU JIT tolerate GCC's built-in _Float* typedefs on Spaces."""
    wrapper_dir = Path("/tmp/mimo_mlx_bin")
    wrapper_dir.mkdir(parents=True, exist_ok=True)
    wrapper = wrapper_dir / "g++"
    if not wrapper.exists():
        wrapper.write_text("#!/usr/bin/env bash\nexec /usr/bin/g++ -fpermissive \"$@\"\n", encoding="utf-8")
        wrapper.chmod(0o755)
    os.environ["PATH"] = f"{wrapper_dir}:{os.environ.get('PATH', '')}"


_install_gxx_wrapper()

import gradio as gr
from mlx_audio.stt import load

MODEL_ID = os.environ.get("MIMO_MLX_MODEL", "mlx-community/MiMo-V2.5-ASR-MLX")
MODEL_DIR = os.environ.get("MIMO_MLX_MODEL_DIR", "/models/MiMo-V2.5-ASR-MLX")
AUDIO_TOKENIZER_DIR = os.environ.get(
    "MIMO_AUDIO_TOKENIZER_DIR",
    "/models/MiMo-Audio-Tokenizer",
)
LANGUAGES = {"Auto": None, "Chinese": "zh", "English": "en"}


def _model_source() -> str:
    if os.path.exists(os.path.join(MODEL_DIR, "config.json")):
        return MODEL_DIR
    return MODEL_ID


@lru_cache(maxsize=1)
def load_asr_model():
    kwargs = {}
    if os.path.exists(os.path.join(AUDIO_TOKENIZER_DIR, "config.json")):
        kwargs["audio_tokenizer_dir"] = AUDIO_TOKENIZER_DIR
    return load(_model_source(), **kwargs)


def transcribe(audio_path: str | None, language_label: str, max_tokens: int):
    if not audio_path:
        return "", "Upload or record an audio file."

    start = time.perf_counter()
    was_loaded = load_asr_model.cache_info().currsize > 0
    load_start = time.perf_counter()
    model = load_asr_model()
    load_elapsed = time.perf_counter() - load_start

    infer_start = time.perf_counter()
    result = model.generate(
        audio_path,
        language=LANGUAGES.get(language_label),
        max_tokens=int(max_tokens),
    )
    infer_elapsed = time.perf_counter() - infer_start
    total_elapsed = time.perf_counter() - start
    load_text = "cached" if was_loaded else f"{load_elapsed:.2f}s"
    status = (
        f"Done. elapsed={total_elapsed:.2f}s, "
        f"model_load={load_text}, infer={infer_elapsed:.2f}s"
    )
    return result.text, status


with gr.Blocks(title="MiMo-V2.5-ASR MLX") as demo:
    gr.Markdown("# MiMo-V2.5-ASR MLX")
    gr.Markdown(
        "This Space uses MLX CPU fallback on Linux. It can verify that the model "
        "loads, but full ASR on cpu-basic may exceed the request timeout. "
        "Run locally on Apple Silicon for the intended MLX GPU path."
    )
    with gr.Row():
        with gr.Column():
            audio = gr.Audio(label="Audio", type="filepath", sources=["upload", "microphone"])
            language = gr.Radio(
                choices=list(LANGUAGES.keys()),
                value="Auto",
                label="Language",
            )
            max_tokens = gr.Slider(1, 128, value=16, step=1, label="Max Tokens")
            button = gr.Button("Transcribe", variant="primary")
        with gr.Column():
            transcript = gr.Textbox(label="Transcript", lines=8)
            status = gr.Textbox(label="Status")

    button.click(transcribe, [audio, language, max_tokens], [transcript, status])


if __name__ == "__main__":
    demo.queue(max_size=4, default_concurrency_limit=1).launch()