ScottzillaSystems commited on
Commit
884e757
·
0 Parent(s):

Fix: OOMKilled → pre-built wheels only, 32K context, multimodal Gemma-4-E4B

Browse files
Files changed (3) hide show
  1. README.md +16 -0
  2. app.py +181 -0
  3. requirements.txt +10 -0
README.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Gemma-4-E4B-Turbo
3
+ emoji: 🤖
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: gradio
7
+ sdk_version: 5.23.3
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ # 🤖 Gemma-4-E4B-Turbo — ZeroGPU
14
+
15
+ **4B parameter model via llama.cpp** — 32K context, multimodal-capable.
16
+ Built with Gradio + `@spaces.GPU` for Hugging Face ZeroGPU.
app.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gemma-4-E4B-Turbo — ZeroGPU / llama.cpp / 32K context + multimodal"""
2
+ import spaces
3
+ import gradio as gr
4
+ from huggingface_hub import hf_hub_download
5
+ from pathlib import Path
6
+ import logging, sys, os, json, tempfile, time
7
+
8
+ logging.basicConfig(level=logging.INFO, stream=sys.stdout)
9
+ logger = logging.getLogger(__name__)
10
+
11
+ MODEL_REPO = "HauhauCS/Gemma-4-E4B-Uncensored-HauhauCS-Aggressive"
12
+ MODEL_FILE = "Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf"
13
+ MMPROJ_FILE = "mmproj-Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf"
14
+
15
+ class ModelManager:
16
+ """Lazy-loading model singleton with error containment."""
17
+ def __init__(self):
18
+ self._llm = None
19
+ self._has_mmproj = False
20
+ self._mmproj_path = None
21
+ self._model_path = None
22
+ self._ready = False
23
+
24
+ def _download(self):
25
+ """Download model files once."""
26
+ if self._model_path:
27
+ return
28
+ logger.info(f"Downloading {MODEL_FILE} from {MODEL_REPO}...")
29
+ self._model_path = hf_hub_download(
30
+ repo_id=MODEL_REPO, filename=MODEL_FILE, resume_download=True
31
+ )
32
+ try:
33
+ self._mmproj_path = hf_hub_download(
34
+ repo_id=MODEL_REPO, filename=MMPROJ_FILE, resume_download=True
35
+ )
36
+ self._has_mmproj = True
37
+ logger.info("mmproj found — multimodal enabled")
38
+ except Exception:
39
+ self._has_mmproj = False
40
+ logger.info("No mmproj — text-only mode")
41
+
42
+ @spaces.GPU(duration=300)
43
+ def load(self):
44
+ """Load llama model on first call (GPU-backed)."""
45
+ if self._ready:
46
+ return self._llm
47
+ self._download()
48
+ logger.info("Loading model into GPU...")
49
+ from llama_cpp import Llama
50
+ kwargs = {
51
+ "model_path": self._model_path,
52
+ "n_gpu_layers": -1, # Offload ALL layers to GPU
53
+ "n_ctx": 32768, # 32K context
54
+ "n_threads": 8,
55
+ "verbose": False,
56
+ "use_mmap": True,
57
+ }
58
+ if self._has_mmproj:
59
+ kwargs["mmproj"] = self._mmproj_path
60
+ self._llm = Llama(**kwargs)
61
+ self._ready = True
62
+ logger.info("Model loaded and ready")
63
+ return self._llm
64
+
65
+ model = ModelManager()
66
+
67
+ @spaces.GPU(duration=300)
68
+ def generate(prompt, max_tokens=1024, temperature=0.7, top_p=0.9, repeat_penalty=1.1):
69
+ try:
70
+ m = model.load()
71
+ out = m(
72
+ prompt,
73
+ max_tokens=min(max_tokens, 8192),
74
+ temperature=temperature,
75
+ top_p=top_p,
76
+ repeat_penalty=repeat_penalty,
77
+ stop=["<|im_end|>", "<|endoftext|>"],
78
+ echo=False,
79
+ )
80
+ return out["choices"][0]["text"].strip()
81
+ except Exception as e:
82
+ logger.error(f"Generate failed: {e}")
83
+ return f"⚠️ Error: {str(e)}"
84
+
85
+ @spaces.GPU(duration=300)
86
+ def chat_respond(message, history, max_tokens=1024, temperature=0.7, top_p=0.9):
87
+ try:
88
+ m = model.load()
89
+ prompt = "<|im_start|>system\nYou are a helpful assistant with 32K context.<|im_end|>\n"
90
+ for h in history:
91
+ prompt += f"<|im_start|>user\n{h[0]}<|im_end|>\n<|im_start|>assistant\n{h[1]}<|im_end|>\n"
92
+ prompt += f"<|im_start|>user\n{message}<|im_end|>\n<|im_start|>assistant\n"
93
+ out = m(
94
+ prompt,
95
+ max_tokens=min(max_tokens, 8192),
96
+ temperature=temperature,
97
+ top_p=top_p,
98
+ stop=["<|im_end|>", "<|endoftext|>"],
99
+ echo=False,
100
+ )
101
+ return out["choices"][0]["text"].strip()
102
+ except Exception as e:
103
+ logger.error(f"Chat failed: {e}")
104
+ return f"⚠️ Error: {str(e)}"
105
+
106
+ @spaces.GPU(duration=300)
107
+ def analyze_image(img, prompt_text):
108
+ if img is None:
109
+ return "Please upload an image first."
110
+ try:
111
+ m = model.load()
112
+ if not model._has_mmproj:
113
+ return "⚠️ Multimodal projection model not available for this GGUF."
114
+ # Convert image to base64 for multimodal
115
+ import base64
116
+ with open(img, "rb") as f:
117
+ b64 = base64.b64encode(f.read()).decode("utf-8")
118
+ ext = Path(img).suffix.lower().lstrip(".")
119
+ if ext in ("jpg", "jpeg"):
120
+ mime = "image/jpeg"
121
+ else:
122
+ mime = f"image/{ext}"
123
+ data_uri = f"data:{mime};base64,{b64}"
124
+ out = m.create_chat_completion(messages=[{
125
+ "role": "user",
126
+ "content": [
127
+ {"type": "text", "text": prompt_text or "Describe this image in detail."},
128
+ {"type": "image_url", "image_url": {"url": data_uri}},
129
+ ],
130
+ }], max_tokens=512, temperature=0.7)
131
+ return out["choices"][0]["message"]["content"]
132
+ except Exception as e:
133
+ logger.error(f"Image analysis failed: {e}")
134
+ return f"⚠️ Error: {str(e)}"
135
+
136
+ with gr.Blocks(
137
+ title="Gemma-4-E4B Turbo (ZeroGPU)",
138
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="green"),
139
+ ) as demo:
140
+ gr.Markdown("# 🤖 Gemma-4-E4B-Turbo · ZeroGPU\n### 32K Context · 4-bit Q5_K_P · Multimodal")
141
+
142
+ with gr.Tabs():
143
+ with gr.Tab("💬 Chat"):
144
+ gr.ChatInterface(
145
+ fn=chat_respond,
146
+ additional_inputs=[
147
+ gr.Slider(128, 8192, value=1024, step=128, label="Max Tokens"),
148
+ gr.Slider(0.1, 2.0, value=0.7, step=0.05, label="Temperature"),
149
+ gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-P"),
150
+ ],
151
+ )
152
+
153
+ with gr.Tab("✍️ Text Generation"):
154
+ with gr.Row():
155
+ with gr.Column(scale=1):
156
+ prompt = gr.Textbox(lines=6, label="📝 Prompt")
157
+ with gr.Row():
158
+ max_tok = gr.Slider(128, 8192, value=1024, step=128, label="Max Tokens")
159
+ temp = gr.Slider(0.1, 2.0, value=0.7, step=0.05, label="Temperature")
160
+ top_p = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-P")
161
+ submit = gr.Button("🚀 Generate", variant="primary")
162
+ with gr.Column(scale=1):
163
+ output = gr.Textbox(lines=20, label="📄 Output")
164
+ submit.click(fn=generate, inputs=[prompt, max_tok, temp, top_p], outputs=output)
165
+ prompt.submit(fn=generate, inputs=[prompt, max_tok, temp, top_p], outputs=output)
166
+
167
+ with gr.Tab("🖼️ Image Analysis"):
168
+ gr.Interface(
169
+ fn=analyze_image,
170
+ inputs=[
171
+ gr.Image(label="Upload Image", type="filepath"),
172
+ gr.Textbox(label="Prompt (optional)", lines=2, placeholder="Describe this image in detail."),
173
+ ],
174
+ outputs=gr.Textbox(lines=15, label="Analysis"),
175
+ title=None,
176
+ allow_flagging="never",
177
+ )
178
+
179
+ gr.Markdown("---\n⚡ **ZeroGPU** | Gemma-4-E4B Q5_K_P | First load downloads model (~3.5GB)")
180
+
181
+ demo.queue(max_size=10).launch()
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Force pre-built ONLY — no source compilation (prevents OOMKilled during build)
2
+ --only-binary :all:
3
+ # Pin to version with verified manylinux x86_64 pre-built wheels
4
+ llama-cpp-python==0.3.2
5
+ # Everything else is pure Python or has wheels
6
+ gradio>=5.23.0,<6.0
7
+ spaces>=0.33
8
+ huggingface-hub>=0.30.0
9
+ Pillow>=10.0.0
10
+ numpy<2.0,>=1.20