vrishv commited on
Commit
e400aec
·
verified ·
1 Parent(s): fb18834

Remove the Chat tab and Gemma: this is a graphics studio

Browse files
Files changed (3) hide show
  1. README.md +1 -8
  2. app.py +1 -119
  3. requirements.txt +0 -1
README.md CHANGED
@@ -8,14 +8,10 @@ app_file: app.py
8
  pinned: false
9
  license: apache-2.0
10
  short_description: FLUX image, LTX video, MusicGen music, Kokoro+Qwen narration
11
- hf_oauth: true
12
- hf_oauth_scopes:
13
- - inference-api
14
  tags:
15
  - text-to-image
16
  - image-to-video
17
  - text-to-video
18
- - text-generation
19
  - text-to-speech
20
  - text-to-audio
21
  - audio-text-to-text
@@ -46,7 +42,6 @@ models:
46
  - hexgrad/Kokoro-82M
47
  - openai/whisper-large-v3-turbo
48
  - laion/clap-htsat-unfused
49
- - google/gemma-4-31B-it
50
  - florence-community/Florence-2-large
51
  - HuggingFaceTB/SmolVLM2-2.2B-Instruct
52
  - Qwen/Qwen3-4B
@@ -62,8 +57,6 @@ models:
62
 
63
  **Narration tab (text to speech, speech generation, machine speech):** Kokoro (hexgrad) reads your text aloud in a choice of eight voices.
64
 
65
- **Chat tab:** Gemma 4 31B (google) answers questions and writes text. It runs in the Space in 8-bit, so no sign-in is needed; a reply uses your own ZeroGPU quota.
66
-
67
  **Describe tab:** three ways to describe what you have.
68
 
69
  - **Image to text (image captioning, image recognition):** Florence-2 large (microsoft, via florence-community) writes a detailed caption of an uploaded picture.
@@ -80,6 +73,6 @@ models:
80
 
81
  **How it stays light on ZeroGPU quota:** each model is kept in its own pack, and a button copies only the models it needs onto the GPU, one after another where a job uses several. An image copies only the image model, and a video loads FLUX, then LTX-Video, then MusicGen in turn, so every job fits the default half-card slice.
82
 
83
- **Licences:** LTX-Video, Kokoro, Whisper, CLAP, Qwen3 4B, Gemma 4 31B, Florence-2 large and SmolVLM2 2.2B are openly licensed. **FLUX.2 klein 9B is under the FLUX Non-Commercial License** and **MusicGen medium is CC-BY-NC 4.0**, so images, videos and music made here are for non-commercial use.
84
 
85
  Runs on ZeroGPU: set the Space hardware to **ZeroGPU** in Settings.
 
8
  pinned: false
9
  license: apache-2.0
10
  short_description: FLUX image, LTX video, MusicGen music, Kokoro+Qwen narration
 
 
 
11
  tags:
12
  - text-to-image
13
  - image-to-video
14
  - text-to-video
 
15
  - text-to-speech
16
  - text-to-audio
17
  - audio-text-to-text
 
42
  - hexgrad/Kokoro-82M
43
  - openai/whisper-large-v3-turbo
44
  - laion/clap-htsat-unfused
 
45
  - florence-community/Florence-2-large
46
  - HuggingFaceTB/SmolVLM2-2.2B-Instruct
47
  - Qwen/Qwen3-4B
 
57
 
58
  **Narration tab (text to speech, speech generation, machine speech):** Kokoro (hexgrad) reads your text aloud in a choice of eight voices.
59
 
 
 
60
  **Describe tab:** three ways to describe what you have.
61
 
62
  - **Image to text (image captioning, image recognition):** Florence-2 large (microsoft, via florence-community) writes a detailed caption of an uploaded picture.
 
73
 
74
  **How it stays light on ZeroGPU quota:** each model is kept in its own pack, and a button copies only the models it needs onto the GPU, one after another where a job uses several. An image copies only the image model, and a video loads FLUX, then LTX-Video, then MusicGen in turn, so every job fits the default half-card slice.
75
 
76
+ **Licences:** LTX-Video, Kokoro, Whisper, CLAP, Qwen3 4B, Florence-2 large and SmolVLM2 2.2B are openly licensed. **FLUX.2 klein 9B is under the FLUX Non-Commercial License** and **MusicGen medium is CC-BY-NC 4.0**, so images, videos and music made here are for non-commercial use.
77
 
78
  Runs on ZeroGPU: set the Space hardware to **ZeroGPU** in Settings.
app.py CHANGED
@@ -5,7 +5,6 @@ Music tab (text to music, text to audio): MusicGen medium turns a description in
5
  Transcribe tab (speech to text, speech recognition, automatic speech recognition): Whisper large v3 turbo
6
  writes down speech from an uploaded file or the microphone, in its own language or translated to English.
7
  Narration tab (text to speech, machine speech, speech generation): Kokoro reads your text aloud.
8
- Chat tab: Gemma 4 31B answers questions, running here in 8-bit (hosted Gemma with sign-in as fallback).
9
  Describe tab: three sub-tabs for the describing jobs the Space does.
10
  Image to text (image captioning, image recognition): Florence-2 large writes a detailed caption for
11
  an uploaded picture.
@@ -23,8 +22,6 @@ Each model has its own ZeroGPU pack (see zero_packs.py), and each button copies
23
  needs onto the GPU, one after another where a job uses several big models. That keeps every job
24
  inside the default half-card slice, which is not billed double. If zero_packs is switched off or
25
  cannot work, everything is copied on every request as ZeroGPU normally does, on the full card.
26
- The Chat tab uses hosted Gemma 4 31B (billed to each visitor's own HF inference credits) and no
27
- ZeroGPU time at all.
28
  """
29
  import os
30
 
@@ -46,7 +43,6 @@ _store = _pick_model_store()
46
  if _store:
47
  os.environ["HF_HOME"] = _store # Must be set before the Hugging Face libraries are imported
48
 
49
- import gc
50
  import json
51
  import random
52
  import re
@@ -67,7 +63,6 @@ import zero_packs as packs
67
  from diffusers import FluxPipeline, LTXConditionPipeline
68
  from diffusers.pipelines.ltx.pipeline_ltx_condition import LTXVideoCondition
69
  from diffusers.utils import export_to_video
70
- from huggingface_hub import InferenceClient
71
  from PIL import Image
72
  from transformers import (AutoModelForCausalLM, AutoModelForImageTextToText, AutoProcessor,
73
  AutoTokenizer, MusicgenForConditionalGeneration, pipeline)
@@ -85,7 +80,6 @@ IMAGE_CAPTION_MODEL = "florence-community/Florence-2-large" # Picture to ca
85
  VIDEO_CAPTION_MODEL = "HuggingFaceTB/SmolVLM2-2.2B-Instruct" # Video frames to description
86
  LISTEN_MODEL = "laion/clap-htsat-unfused" # Recognises genre, mood, instruments and tempo
87
  SPEECH_MODEL = "openai/whisper-large-v3-turbo" # Speech to text
88
- CHAT_MODEL = "google/gemma-4-31B-it" # Runs here in 8-bit; hosted inference is the fallback
89
  VOICE = "af_heart" # Kokoro voice
90
  VOICE_RATE = 24000
91
  VOICES = {"Heart (woman)": "af_heart", "Bella (woman)": "af_bella", "Nicole (woman, soft)": "af_nicole",
@@ -112,34 +106,6 @@ SPEED_SUFFIX = ", moving at natural real-time speed"
112
  # Pure per-call .to('cuda') from ordinary RAM was tried on 2026-10-07: the first FLUX move took 79 s.
113
  # The packs are read straight from disk at about 17 GB a second, like ZeroGPU's own single pack.
114
  # Once a model is sealed, never call .to("cuda") on it again here: its weights are on disk.
115
- # Chat: Gemma 4 31B with its weights squeezed to 8-bit by bitsandbytes (about 32 GB, so it fits the
116
- # half card on its own). The squeezing is done here on the CPU at start-up, which takes a few minutes.
117
- # If anything goes wrong the Space still starts, and the Chat tab uses hosted Gemma with sign-in.
118
- # It is loaded first, while the disk is empty: on 2026-10-09 downloading Gemma (62 GB) after the other
119
- # models went over the Space's 150 GB disk limit and the Space was evicted. Every model's downloaded
120
- # files are now deleted as soon as its pack is sealed. Set the Space variable LOCAL_CHAT to "0" to skip it.
121
- try: # Which disks there are and how full: the packs' disk and the download cache have separate limits
122
- print("[disk]", subprocess.run(["df", "-h"], capture_output=True, text=True).stdout.replace("\n", " | "), flush=True)
123
- except Exception as e:
124
- print(f"[disk] df failed: {e}", flush=True)
125
- chat_model = chat_processor = None
126
- try:
127
- if not packs.enabled or os.environ.get("LOCAL_CHAT", "0") != "1":
128
- raise RuntimeError("switched off")
129
- print("[load] Gemma 4 31B (8-bit)...", flush=True)
130
- from transformers import BitsAndBytesConfig
131
- _t0 = time.time()
132
- chat_processor = AutoProcessor.from_pretrained(CHAT_MODEL)
133
- chat_model = AutoModelForImageTextToText.from_pretrained(
134
- CHAT_MODEL, quantization_config=BitsAndBytesConfig(load_in_8bit=True),
135
- torch_dtype=torch.bfloat16, device_map={"": "cuda"})
136
- print(f"[load] Gemma ready in {time.time() - _t0:.0f}s", flush=True)
137
- packs.seal("gemma", CHAT_MODEL)
138
- except Exception as e:
139
- print(f"[load] no local Gemma, Chat will use hosted Gemma: {type(e).__name__}: {str(e)[:300]}", flush=True)
140
- chat_model = None
141
- gc.collect() # Drop any half-loaded weights, so they are not packed with the next model
142
- packs.free_downloads(CHAT_MODEL) # And its downloaded files, so the disk limit is not hit later
143
  print("[load] image model...", flush=True)
144
  try:
145
  from diffusers import Flux2KleinPipeline
@@ -203,15 +169,6 @@ speech_pipe = pipeline("automatic-speech-recognition", model=SPEECH_MODEL, torch
203
  device="cuda", chunk_length_s=30)
204
  packs.seal("whisper", SPEECH_MODEL)
205
  print("[load] done.", flush=True)
206
- try: # Where the downloads and packs really are, and what counts towards the disk limits
207
- from huggingface_hub import constants as _hub
208
- _script = ("du -xsh /root/.cache/* /root/.cache/huggingface/* /root/.zerogpu /data-nvme/* /tmp /home/* 2>/dev/null"
209
- " | sort -h | tail -25; uname -r; free -g | head -2")
210
- print("[where]", f"HF_HUB_CACHE={_hub.HF_HUB_CACHE} HOME={os.path.expanduser('~')} offload={packs.PACK_DIR}", "|",
211
- subprocess.run(["sh", "-c", _script], capture_output=True, text=True, timeout=120).stdout.replace("\n", " | "),
212
- flush=True)
213
- except Exception as e:
214
- print(f"[where] failed: {e}", flush=True)
215
 
216
  # With a pack per model every job fits the default half-card slice ("large"), which is billed once.
217
  # Without them ZeroGPU copies all the models on every call, and that needs the whole card.
@@ -529,71 +486,6 @@ def video_frames(path, count=6):
529
  return frames, length
530
 
531
 
532
- # ---------------------------------------------------------------- Chat (hosted Gemma; no GPU)
533
-
534
- def as_text(content):
535
- if isinstance(content, str):
536
- return content
537
- if isinstance(content, dict):
538
- return str(content.get("text", ""))
539
- if isinstance(content, (list, tuple)):
540
- return " ".join(as_text(part) for part in content)
541
- return str(content or "")
542
-
543
-
544
- def chat_seconds(messages):
545
- return gpu_seconds(60)
546
-
547
-
548
- @spaces.GPU(duration=chat_seconds, size=GPU_SIZE)
549
- def chat_gpu(messages):
550
- """Gemma 4 31B (8-bit) answers on the GPU."""
551
- with timed("chat"):
552
- packs.load("gemma")
553
- turns = [{"role": m["role"], "content": [{"type": "text", "text": m["content"]}]} for m in messages]
554
- inputs = chat_processor.apply_chat_template(
555
- turns, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt").to("cuda")
556
- start = time.time()
557
- out = chat_model.generate(**inputs, max_new_tokens=300, do_sample=True, temperature=0.7, top_p=0.9)
558
- new = out[0][inputs["input_ids"].shape[1]:]
559
- print(f"[chat] {len(new)} tokens at {len(new) / (time.time() - start):.1f} a second", flush=True)
560
- return chat_processor.decode(new, skip_special_tokens=True).strip()
561
-
562
-
563
- def chat(message, history, oauth_token: gr.OAuthToken | None = None):
564
- """Gemma 4 31B. Runs here in 8-bit; if that could not be loaded, hosted Gemma with the visitor's sign-in."""
565
- if chat_model is not None:
566
- messages = [{"role": "system", "content": "You are a helpful, friendly assistant. Answer clearly and briefly."}]
567
- for turn in history[-8:]:
568
- if isinstance(turn, dict):
569
- messages.append({"role": turn.get("role", "user"), "content": as_text(turn.get("content"))})
570
- messages.append({"role": "user", "content": as_text(message)})
571
- return chat_gpu(messages)
572
- if oauth_token is None:
573
- return ("Please press **Sign in with Hugging Face** above. Each visitor's chat reply is billed to their "
574
- "own Hugging Face inference credits, not the owner's — so sign-in is required for this tab.")
575
- messages = [{"role": "system",
576
- "content": "You are a helpful, friendly assistant. Answer clearly and briefly."}]
577
- for turn in history[-12:]:
578
- if isinstance(turn, dict):
579
- messages.append({"role": turn.get("role", "user"), "content": as_text(turn.get("content"))})
580
- else:
581
- messages.append({"role": "user", "content": as_text(turn[0])})
582
- if turn[1]:
583
- messages.append({"role": "assistant", "content": as_text(turn[1])})
584
- messages.append({"role": "user", "content": as_text(message)})
585
- try:
586
- client = InferenceClient(token=oauth_token.token)
587
- out = client.chat_completion(model=CHAT_MODEL, messages=messages, max_tokens=700)
588
- return out.choices[0].message.content.strip()
589
- except Exception as e:
590
- name = type(e).__name__
591
- msg = str(e)
592
- if "credits" in msg.lower() or "quota" in msg.lower() or "402" in msg:
593
- return "Your Hugging Face inference credits are used up for now. Please try again later."
594
- return f"The chat service could not be reached ({name}). Please try again in a moment."
595
-
596
-
597
  # ---------------------------------------------------------------- The buttons
598
 
599
  @spaces.GPU(duration=gpu_seconds(7), size=GPU_SIZE)
@@ -829,7 +721,7 @@ def on_open():
829
 
830
  with gr.Blocks(title="Graphics Studio") as demo:
831
  gr.Markdown("# Graphics Studio\n**FLUX.2 klein** images, **LTX-Video** animation, **MusicGen** music, "
832
- "**Qwen3** + **Kokoro** narration, **Whisper** transcription, **Gemma 4 31B** chat, "
833
  "**Florence-2** image captions, **SmolVLM2** video descriptions, **CLAP** music classification.")
834
  counter = gr.Markdown(counts_text())
835
 
@@ -882,16 +774,6 @@ with gr.Blocks(title="Graphics Studio") as demo:
882
  read_button = gr.Button("Read aloud", variant="primary")
883
  read_out = gr.Audio(label="Narration", type="filepath", autoplay=False)
884
 
885
- with gr.Tab("Chat"):
886
- if chat_model is not None:
887
- gr.Markdown("Chat with **Gemma 4 31B** (google), running here in 8-bit. No sign-in needed. "
888
- "A reply takes a while and uses your own ZeroGPU quota.")
889
- else:
890
- gr.Markdown("Chat with hosted **Gemma 4 31B** (google). Sign in with Hugging Face first — each visitor's "
891
- "reply is billed to their own Hugging Face inference credits, not the owner's.")
892
- gr.LoginButton()
893
- gr.ChatInterface(chat)
894
-
895
  with gr.Tab("Describe"):
896
  gr.Markdown("Three ways to describe what you have: an image (image captioning), a short video "
897
  "(video captioning), or a piece of music (music captioning, music classification). "
 
5
  Transcribe tab (speech to text, speech recognition, automatic speech recognition): Whisper large v3 turbo
6
  writes down speech from an uploaded file or the microphone, in its own language or translated to English.
7
  Narration tab (text to speech, machine speech, speech generation): Kokoro reads your text aloud.
 
8
  Describe tab: three sub-tabs for the describing jobs the Space does.
9
  Image to text (image captioning, image recognition): Florence-2 large writes a detailed caption for
10
  an uploaded picture.
 
22
  needs onto the GPU, one after another where a job uses several big models. That keeps every job
23
  inside the default half-card slice, which is not billed double. If zero_packs is switched off or
24
  cannot work, everything is copied on every request as ZeroGPU normally does, on the full card.
 
 
25
  """
26
  import os
27
 
 
43
  if _store:
44
  os.environ["HF_HOME"] = _store # Must be set before the Hugging Face libraries are imported
45
 
 
46
  import json
47
  import random
48
  import re
 
63
  from diffusers import FluxPipeline, LTXConditionPipeline
64
  from diffusers.pipelines.ltx.pipeline_ltx_condition import LTXVideoCondition
65
  from diffusers.utils import export_to_video
 
66
  from PIL import Image
67
  from transformers import (AutoModelForCausalLM, AutoModelForImageTextToText, AutoProcessor,
68
  AutoTokenizer, MusicgenForConditionalGeneration, pipeline)
 
80
  VIDEO_CAPTION_MODEL = "HuggingFaceTB/SmolVLM2-2.2B-Instruct" # Video frames to description
81
  LISTEN_MODEL = "laion/clap-htsat-unfused" # Recognises genre, mood, instruments and tempo
82
  SPEECH_MODEL = "openai/whisper-large-v3-turbo" # Speech to text
 
83
  VOICE = "af_heart" # Kokoro voice
84
  VOICE_RATE = 24000
85
  VOICES = {"Heart (woman)": "af_heart", "Bella (woman)": "af_bella", "Nicole (woman, soft)": "af_nicole",
 
106
  # Pure per-call .to('cuda') from ordinary RAM was tried on 2026-10-07: the first FLUX move took 79 s.
107
  # The packs are read straight from disk at about 17 GB a second, like ZeroGPU's own single pack.
108
  # Once a model is sealed, never call .to("cuda") on it again here: its weights are on disk.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  print("[load] image model...", flush=True)
110
  try:
111
  from diffusers import Flux2KleinPipeline
 
169
  device="cuda", chunk_length_s=30)
170
  packs.seal("whisper", SPEECH_MODEL)
171
  print("[load] done.", flush=True)
 
 
 
 
 
 
 
 
 
172
 
173
  # With a pack per model every job fits the default half-card slice ("large"), which is billed once.
174
  # Without them ZeroGPU copies all the models on every call, and that needs the whole card.
 
486
  return frames, length
487
 
488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  # ---------------------------------------------------------------- The buttons
490
 
491
  @spaces.GPU(duration=gpu_seconds(7), size=GPU_SIZE)
 
721
 
722
  with gr.Blocks(title="Graphics Studio") as demo:
723
  gr.Markdown("# Graphics Studio\n**FLUX.2 klein** images, **LTX-Video** animation, **MusicGen** music, "
724
+ "**Qwen3** + **Kokoro** narration, **Whisper** transcription, "
725
  "**Florence-2** image captions, **SmolVLM2** video descriptions, **CLAP** music classification.")
726
  counter = gr.Markdown(counts_text())
727
 
 
774
  read_button = gr.Button("Read aloud", variant="primary")
775
  read_out = gr.Audio(label="Narration", type="filepath", autoplay=False)
776
 
 
 
 
 
 
 
 
 
 
 
777
  with gr.Tab("Describe"):
778
  gr.Markdown("Three ways to describe what you have: an image (image captioning), a short video "
779
  "(video captioning), or a piece of music (music captioning, music classification). "
requirements.txt CHANGED
@@ -15,4 +15,3 @@ huggingface_hub>=0.24.0
15
  timm
16
  einops
17
  num2words
18
- bitsandbytes>=0.46.0
 
15
  timm
16
  einops
17
  num2words