mazesmazes commited on
Commit
f3d63ca
·
verified ·
1 Parent(s): d96d2bf

Deploy demo to HF Space

Browse files
README.md CHANGED
@@ -19,13 +19,6 @@ tags:
19
  - qwen3.5
20
  - lora
21
  - mlp
22
- suggested_hardware: zero-a10g
23
- preload_from_hub:
24
- - mazesmazes/tiny-audio
25
- - ibm-granite/granite-speech-5.0-470m-turboctc
26
- - Qwen/Qwen3.5-2B
27
- - Qwen/Qwen3-ForcedAligner-0.6B-hf
28
- - nvidia/Nemotron-3-Diarization
29
  ---
30
 
31
  ## Demo Overview
@@ -64,8 +57,8 @@ The model bridges audio and text with a trained projector and LoRA adapters:
64
 
65
  - Plain transcription works best on clips up to about 19 seconds (the training length); with
66
  timestamps or diarization on, longer audio is transcribed in chunks automatically
67
- - Each request reserves GPU time from your ZeroGPU quota in proportion to the audio's length (20-120
68
- s), so roughly up to five minutes of speech per request
69
  - Optimized for English language
70
  - Best performance with clear speech and minimal background noise
71
 
 
19
  - qwen3.5
20
  - lora
21
  - mlp
 
 
 
 
 
 
 
22
  ---
23
 
24
  ## Demo Overview
 
57
 
58
  - Plain transcription works best on clips up to about 19 seconds (the training length); with
59
  timestamps or diarization on, longer audio is transcribed in chunks automatically
60
+ - Requests run on a GPU server on RunPod; while it is starting up or offline, requests fail with a
61
+ "try again" message
62
  - Optimized for English language
63
  - Best performance with clear speech and minimal background noise
64
 
__pycache__/app.cpython-311.pyc ADDED
Binary file (26.3 kB). View file
 
__pycache__/app.cpython-312.pyc CHANGED
Binary files a/__pycache__/app.cpython-312.pyc and b/__pycache__/app.cpython-312.pyc differ
 
app.py CHANGED
@@ -5,6 +5,9 @@ Gradio app for ASR model with support for:
5
  - File upload
6
  - Word-level timestamps
7
  - Speaker diarization
 
 
 
8
  """
9
 
10
  import os
@@ -29,6 +32,7 @@ os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib"
29
  # Disable tokenizer parallelism warning
30
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
31
 
 
32
  import html
33
  import sys
34
  from collections.abc import Callable, Mapping, Sequence
@@ -38,11 +42,10 @@ from pathlib import Path
38
  from typing import Annotated, Any, NotRequired, TypedDict, cast
39
 
40
  import gradio as gr
 
41
  import soundfile
42
- import torch
43
  import typer
44
  from gradio import themes
45
- from transformers import pipeline
46
 
47
 
48
  class Word(TypedDict):
@@ -209,6 +212,8 @@ Model: <a href="https://huggingface.co/{model}" target="_blank">{model}</a></p>
209
 
210
  def pick_device() -> int | str:
211
  """The pipeline device: CUDA index 0, Apple "mps", or -1 for CPU."""
 
 
212
  if torch.cuda.is_available():
213
  return 0
214
  if torch.backends.mps.is_available():
@@ -310,8 +315,9 @@ def build_output_tabs() -> tuple[gr.Tabs, gr.Textbox, gr.HTML, gr.Dataframe, gr.
310
  return tabs, output_text, conversation_output, timestamps_output, diarization_output
311
 
312
 
313
- def create_demo(model_path: str = "mazesmazes/tiny-audio") -> gr.Blocks:
314
- """Create Gradio demo interface using transformers pipeline."""
 
315
 
316
  # Load pipeline - uses custom ASRPipeline from the model repo
317
  pipe = pipeline(
@@ -333,6 +339,55 @@ def create_demo(model_path: str = "mazesmazes/tiny-audio") -> gr.Blocks:
333
  # pipeline's __call__ with the batched (list) return type.
334
  return cast(dict[str, Any], pipe(audio, **kwargs))
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  def process_audio(
337
  audio: str | None,
338
  show_timestamps: bool,
@@ -347,7 +402,6 @@ def create_demo(model_path: str = "mazesmazes/tiny-audio") -> gr.Blocks:
347
 
348
  kwargs = pipeline_kwargs(show_timestamps, show_diarization, num_speakers, max_speakers)
349
 
350
- # Transcribe the audio (on the GPU, on ZeroGPU)
351
  result = run_pipeline(audio, kwargs)
352
  warn_partial_failures(result)
353
 
@@ -411,11 +465,19 @@ def main(
411
  str,
412
  typer.Option("--model", "-m", envvar="MODEL_ID", help="HuggingFace Hub model ID"),
413
  ] = "mazesmazes/tiny-audio",
 
 
 
 
 
 
 
 
414
  port: Annotated[int, typer.Option("--port", "-p", help="Server port")] = 7860,
415
  share: Annotated[bool, typer.Option("--share", help="Create public share link")] = False,
416
  ) -> None:
417
  """Launch ASR Gradio demo."""
418
- demo = create_demo(model)
419
  demo.launch(server_port=port, share=share, server_name="0.0.0.0", theme=THEME, css=CSS)
420
 
421
 
 
5
  - File upload
6
  - Word-level timestamps
7
  - Speaker diarization
8
+
9
+ With ENDPOINT_URL set, requests go to a tiny-audio server (`ta serve`) and this
10
+ app needs no GPU, torch or transformers; without it the model runs in-process.
11
  """
12
 
13
  import os
 
32
  # Disable tokenizer parallelism warning
33
  os.environ["TOKENIZERS_PARALLELISM"] = "false"
34
 
35
+ import base64
36
  import html
37
  import sys
38
  from collections.abc import Callable, Mapping, Sequence
 
42
  from typing import Annotated, Any, NotRequired, TypedDict, cast
43
 
44
  import gradio as gr
45
+ import httpx
46
  import soundfile
 
47
  import typer
48
  from gradio import themes
 
49
 
50
 
51
  class Word(TypedDict):
 
212
 
213
  def pick_device() -> int | str:
214
  """The pipeline device: CUDA index 0, Apple "mps", or -1 for CPU."""
215
+ import torch # noqa: PLC0415 -- server mode runs without torch
216
+
217
  if torch.cuda.is_available():
218
  return 0
219
  if torch.backends.mps.is_available():
 
315
  return tabs, output_text, conversation_output, timestamps_output, diarization_output
316
 
317
 
318
+ def local_runner(model_path: str) -> Callable[[str, dict[str, Any]], dict[str, Any]]:
319
+ """Run the pipeline in this process (on the GPU, on ZeroGPU)."""
320
+ from transformers import pipeline # noqa: PLC0415 -- server mode runs without it
321
 
322
  # Load pipeline - uses custom ASRPipeline from the model repo
323
  pipe = pipeline(
 
339
  # pipeline's __call__ with the batched (list) return type.
340
  return cast(dict[str, Any], pipe(audio, **kwargs))
341
 
342
+ return run_pipeline
343
+
344
+
345
+ # The RunPod proxy answers 502-504 while the pod's server is down or still
346
+ # loading its models; anything else is a real failure.
347
+ WAKING_UP = "The model server is starting up or offline. Please try again in a few minutes."
348
+
349
+
350
+ def remote_runner(
351
+ endpoint_url: str, api_key: str | None
352
+ ) -> Callable[[str, dict[str, Any]], dict[str, Any]]:
353
+ """POST each request to a `ta serve` server (scripts/serve.py)."""
354
+ headers = {"Authorization": f"Bearer {api_key}"} if api_key else {}
355
+
356
+ def run_pipeline(audio: str, kwargs: dict[str, Any]) -> dict[str, Any]:
357
+ # A JSON body is the only way to send `parameters` with the audio,
358
+ # so the audio travels base64-encoded.
359
+ payload = {
360
+ "inputs": base64.b64encode(Path(audio).read_bytes()).decode(),
361
+ "parameters": kwargs,
362
+ }
363
+ try:
364
+ response = httpx.post(endpoint_url, json=payload, headers=headers, timeout=600)
365
+ except httpx.TimeoutException as e:
366
+ msg = "The model server took too long to respond."
367
+ raise gr.Error(msg) from e
368
+ except httpx.HTTPError as e:
369
+ msg = f"Could not reach the model server: {e}"
370
+ raise gr.Error(msg) from e
371
+ if response.status_code in (502, 503, 504):
372
+ raise gr.Error(WAKING_UP)
373
+ if response.is_error:
374
+ msg = f"Model server error {response.status_code}: {response.text[:300]}"
375
+ raise gr.Error(msg)
376
+ return cast(dict[str, Any], response.json())
377
+
378
+ return run_pipeline
379
+
380
+
381
+ def create_demo(
382
+ model_path: str = "mazesmazes/tiny-audio",
383
+ endpoint_url: str | None = None,
384
+ api_key: str | None = None,
385
+ ) -> gr.Blocks:
386
+ """Create the Gradio demo, backed by a `ta serve` server or a local pipeline."""
387
+ run_pipeline = (
388
+ remote_runner(endpoint_url, api_key) if endpoint_url else local_runner(model_path)
389
+ )
390
+
391
  def process_audio(
392
  audio: str | None,
393
  show_timestamps: bool,
 
402
 
403
  kwargs = pipeline_kwargs(show_timestamps, show_diarization, num_speakers, max_speakers)
404
 
 
405
  result = run_pipeline(audio, kwargs)
406
  warn_partial_failures(result)
407
 
 
465
  str,
466
  typer.Option("--model", "-m", envvar="MODEL_ID", help="HuggingFace Hub model ID"),
467
  ] = "mazesmazes/tiny-audio",
468
+ endpoint_url: Annotated[
469
+ str | None,
470
+ typer.Option(
471
+ "--endpoint-url",
472
+ envvar="ENDPOINT_URL",
473
+ help="`ta serve` URL to send requests to instead of loading the model",
474
+ ),
475
+ ] = None,
476
  port: Annotated[int, typer.Option("--port", "-p", help="Server port")] = 7860,
477
  share: Annotated[bool, typer.Option("--share", help="Create public share link")] = False,
478
  ) -> None:
479
  """Launch ASR Gradio demo."""
480
+ demo = create_demo(model, endpoint_url, os.environ.get("TINY_AUDIO_API_KEY"))
481
  demo.launch(server_port=port, share=share, server_name="0.0.0.0", theme=THEME, css=CSS)
482
 
483
 
requirements.txt CHANGED
@@ -1,14 +1,7 @@
1
- # Speaker diarization uses Nemotron-3-Diarization, in transformers main after 5.17.
2
- # Pinned to the commit the pipeline was verified on; move to a release once one ships it.
3
- transformers @ git+https://github.com/huggingface/transformers@91ea1192a00e0ebb684400434375fbd2345c8449
4
- torch
5
- torchaudio
6
- peft
7
- safetensors
8
- numpy
9
- librosa
10
  soundfile
11
- spaces
12
- # Fast path for Qwen3.5's gated-delta-rule layers (transformers falls back to a
13
- # slower reference implementation without it). Pure Triton, nothing to compile.
14
- flash-linear-attention>=0.2.2
 
1
+ # The Space sends requests to a tiny-audio server on RunPod (ENDPOINT_URL, `ta serve`),
2
+ # so it needs no torch or transformers. gradio itself comes from the README's sdk_version.
3
+ httpx
 
 
 
 
 
 
4
  soundfile
5
+ typer
6
+ # Running the model in-process instead (no ENDPOINT_URL, e.g. `ta demo` locally)
7
+ # needs the model's own dependencies: see the repo's requirements.txt.