Spaces:
Running on Zero
Running on Zero
ImageStudio Maintainer Claude Opus 4.8 (1M context) commited on
Commit ·
c5fc943
1
Parent(s): c4415be
feat: switch assistant VLM Qwen3.5-4B -> gemma-4-E4B-it-Uncensored-MAX
Browse filesComment out the Qwen model (kept for revert) and load prithivMLmods/
gemma-4-E4B-it-Uncensored-MAX (Gemma4ForConditionalGeneration, multimodal).
Make the eos-token resolver model-agnostic: union the model's own
generation_config eos, the tokenizer eos, and known turn terminators across
families (Gemma <turn|>, Qwen <|im_end|>, Llama <|eot_id|>), skipping <unk>.
Gemma ships a generation_config so it stops natively; the union keeps the
manual-pin safety for models that don't. Update assistant UI copy; Gemma has
no thinking mode so the Reasoning toggle is now a documented no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
app.py
CHANGED
|
@@ -140,14 +140,17 @@ noobxl_pipe.to("cuda")
|
|
| 140 |
print("Pipelines loaded!")
|
| 141 |
|
| 142 |
# -----------------------------------------------------------------------------
|
| 143 |
-
#
|
| 144 |
-
#
|
| 145 |
-
#
|
| 146 |
-
#
|
| 147 |
# -----------------------------------------------------------------------------
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
-
print("Loading
|
| 151 |
vlm_processor = AutoProcessor.from_pretrained(
|
| 152 |
VLM_MODEL_ID, token=HF_TOKEN, trust_remote_code=True
|
| 153 |
)
|
|
@@ -159,17 +162,37 @@ vlm_model = AutoModelForImageTextToText.from_pretrained(
|
|
| 159 |
)
|
| 160 |
vlm_model.to("cuda").eval()
|
| 161 |
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
_vlm_tokenizer = getattr(vlm_processor, "tokenizer", vlm_processor)
|
| 168 |
-
_VLM_EOS_IDS =
|
| 169 |
-
tid for tok in ("<|im_end|>", "<|endoftext|>")
|
| 170 |
-
for tid in (_vlm_tokenizer.convert_tokens_to_ids(tok),)
|
| 171 |
-
if isinstance(tid, int) and tid >= 0
|
| 172 |
-
} | ({_vlm_tokenizer.eos_token_id} if _vlm_tokenizer.eos_token_id is not None else set()))
|
| 173 |
_VLM_PAD_ID = (
|
| 174 |
_vlm_tokenizer.pad_token_id
|
| 175 |
if _vlm_tokenizer.pad_token_id is not None
|
|
@@ -1306,7 +1329,7 @@ with gr.Blocks(fill_height=True) as demo:
|
|
| 1306 |
|
| 1307 |
with gr.Tab("💬 Prompt Assistant"):
|
| 1308 |
gr.Markdown(
|
| 1309 |
-
"Ask **
|
| 1310 |
"Turn a rough idea into a rich prompt, or upload a reference image and "
|
| 1311 |
"ask the model to describe it as a prompt."
|
| 1312 |
)
|
|
@@ -1327,7 +1350,7 @@ with gr.Blocks(fill_height=True) as demo:
|
|
| 1327 |
choices=["Off", "On"],
|
| 1328 |
value="Off",
|
| 1329 |
label="🧠 Reasoning",
|
| 1330 |
-
info="
|
| 1331 |
)
|
| 1332 |
with gr.Accordion("⚙️ Settings", open=False):
|
| 1333 |
vlm_max_tokens = gr.Slider(
|
|
|
|
| 140 |
print("Pipelines loaded!")
|
| 141 |
|
| 142 |
# -----------------------------------------------------------------------------
|
| 143 |
+
# Multimodal assistant. Powers the "Prompt Assistant" tab: one-turn question
|
| 144 |
+
# answering with an optional image, for turning rough ideas into rich prompts or
|
| 145 |
+
# describing a reference image. Loaded once, bf16 — fits alongside the two
|
| 146 |
+
# diffusion pipelines on the shared GPU.
|
| 147 |
# -----------------------------------------------------------------------------
|
| 148 |
+
# Previous model (Qwen3.5-4B) — kept for easy revert. It shipped no
|
| 149 |
+
# generation_config.json (needed manual eos pinning, see _resolve_vlm_eos_ids).
|
| 150 |
+
# VLM_MODEL_ID = "rodrigomt/Qwen3.5-4B-Uncensored-Aggressive"
|
| 151 |
+
VLM_MODEL_ID = "prithivMLmods/gemma-4-E4B-it-Uncensored-MAX"
|
| 152 |
|
| 153 |
+
print(f"Loading assistant: {VLM_MODEL_ID} ...")
|
| 154 |
vlm_processor = AutoProcessor.from_pretrained(
|
| 155 |
VLM_MODEL_ID, token=HF_TOKEN, trust_remote_code=True
|
| 156 |
)
|
|
|
|
| 162 |
)
|
| 163 |
vlm_model.to("cuda").eval()
|
| 164 |
|
| 165 |
+
|
| 166 |
+
def _resolve_vlm_eos_ids(tokenizer, model):
|
| 167 |
+
"""Collect every stop token id for the active VLM, model-agnostically.
|
| 168 |
+
|
| 169 |
+
generate() stops on these. We union three sources so a model swap can't
|
| 170 |
+
silently lose its terminator: (1) the model's own generation_config (correct
|
| 171 |
+
for models that ship one, e.g. Gemma's <turn|>/<eos>), (2) the tokenizer's
|
| 172 |
+
eos, and (3) known chat-turn terminators across families (Gemma's <turn|>,
|
| 173 |
+
Qwen's <|im_end|>, Llama's <|eot_id|>). Tokens absent from the vocab resolve
|
| 174 |
+
to <unk> and are skipped. This is what stops a model like the old Qwen
|
| 175 |
+
finetune (no generation_config) from rambling to max_new_tokens.
|
| 176 |
+
"""
|
| 177 |
+
ids = set()
|
| 178 |
+
gc = getattr(model, "generation_config", None)
|
| 179 |
+
gc_eos = getattr(gc, "eos_token_id", None) if gc is not None else None
|
| 180 |
+
if isinstance(gc_eos, int):
|
| 181 |
+
ids.add(gc_eos)
|
| 182 |
+
elif isinstance(gc_eos, (list, tuple)):
|
| 183 |
+
ids.update(int(x) for x in gc_eos)
|
| 184 |
+
if tokenizer.eos_token_id is not None:
|
| 185 |
+
ids.add(int(tokenizer.eos_token_id))
|
| 186 |
+
unk = tokenizer.unk_token_id
|
| 187 |
+
for t in ("<turn|>", "<end_of_turn>", "<|im_end|>", "<|eot_id|>", "<|endoftext|>"):
|
| 188 |
+
tid = tokenizer.convert_tokens_to_ids(t)
|
| 189 |
+
if isinstance(tid, int) and tid >= 0 and tid != unk:
|
| 190 |
+
ids.add(tid)
|
| 191 |
+
return sorted(ids)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
_vlm_tokenizer = getattr(vlm_processor, "tokenizer", vlm_processor)
|
| 195 |
+
_VLM_EOS_IDS = _resolve_vlm_eos_ids(_vlm_tokenizer, vlm_model)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
_VLM_PAD_ID = (
|
| 197 |
_vlm_tokenizer.pad_token_id
|
| 198 |
if _vlm_tokenizer.pad_token_id is not None
|
|
|
|
| 1329 |
|
| 1330 |
with gr.Tab("💬 Prompt Assistant"):
|
| 1331 |
gr.Markdown(
|
| 1332 |
+
"Ask **Gemma 4 E4B** a single question — with or without an image. "
|
| 1333 |
"Turn a rough idea into a rich prompt, or upload a reference image and "
|
| 1334 |
"ask the model to describe it as a prompt."
|
| 1335 |
)
|
|
|
|
| 1350 |
choices=["Off", "On"],
|
| 1351 |
value="Off",
|
| 1352 |
label="🧠 Reasoning",
|
| 1353 |
+
info="Direct answers are best for prompts. (Reasoning mode only applies to thinking-capable models; Gemma 4 answers directly.)",
|
| 1354 |
)
|
| 1355 |
with gr.Accordion("⚙️ Settings", open=False):
|
| 1356 |
vlm_max_tokens = gr.Slider(
|