Spaces:
Running on Zero
Running on Zero
Switch assistant to gemma-4-12B (8-bit) + expose system prompt
Browse files- VLM_MODEL_ID -> OpenYourMind/gemma-4-12B-it-abliterated-uncensored
(model_type gemma4_unified). Load 8-bit via bitsandbytes by default
(~12GB, fits the zero-a10g alongside the diffusion pipelines);
VLM_LOAD_8BIT=0 reverts to bf16. Prior models kept commented.
- requirements: transformers -> git main (gemma4_unified support) + bitsandbytes.
- Expose an optional system_prompt through _vlm_chat_core (real system role
when the template supports it, folded into the user turn otherwise) and
surface it in the Prompt Assistant UI + the prompt_assistant /
prompt_to_video_assets / image_to_video_assets API endpoints.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- app.py +95 -44
- requirements.txt +3 -1
app.py
CHANGED
|
@@ -20,7 +20,12 @@ from diffusers import (
|
|
| 20 |
)
|
| 21 |
from compel import Compel, ReturnedEmbeddingsType
|
| 22 |
from huggingface_hub import hf_hub_download
|
| 23 |
-
from transformers import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
# Structured progress contract (also see wan2.2 / LTX2.3 Spaces and the generator
|
| 26 |
# orchestrator). Every streaming endpoint yields a hidden JSON "progress" output
|
|
@@ -145,26 +150,36 @@ print("Pipelines loaded!")
|
|
| 145 |
# describing a reference image. Loaded once, bf16 — fits alongside the two
|
| 146 |
# diffusion pipelines on the shared GPU.
|
| 147 |
# -----------------------------------------------------------------------------
|
| 148 |
-
# Prior models, kept for easy revert:
|
| 149 |
# "rodrigomt/Qwen3.5-4B-Uncensored-Aggressive" # no generation_config; needed eos pinning
|
| 150 |
-
# "prithivMLmods/gemma-4-E4B-it-Uncensored-MAX" # under-rated explicit content (
|
| 151 |
-
#
|
| 152 |
-
#
|
| 153 |
-
#
|
| 154 |
-
#
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
vlm_processor = AutoProcessor.from_pretrained(
|
| 159 |
VLM_MODEL_ID, token=HF_TOKEN, trust_remote_code=True
|
| 160 |
)
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
)
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
|
| 170 |
def _resolve_vlm_eos_ids(tokenizer, model):
|
|
@@ -671,15 +686,21 @@ def _generate_image_inner(
|
|
| 671 |
# =============================================================================
|
| 672 |
# Prompt Assistant (Qwen3.5-4B) — single-turn chat, optional image
|
| 673 |
# =============================================================================
|
| 674 |
-
def _vlm_chat_core(message, image, reasoning, max_new_tokens):
|
| 675 |
"""Token-streaming VLM chat body (no GPU decorator).
|
| 676 |
|
| 677 |
Yields ``("progress", produced, budget, partial)`` per token and a final
|
| 678 |
``("text", answer)`` tuple. Kept decorator-free so it can be reused inside a
|
| 679 |
*single* ``@spaces.GPU`` window by other endpoints (e.g. the image→video
|
| 680 |
check), avoiding a second ZeroGPU acquisition. See :func:`vlm_chat`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
"""
|
| 682 |
message = (message or "").strip()
|
|
|
|
| 683 |
if not message and image is None:
|
| 684 |
yield ("text", "Please enter a question (and optionally attach an image).")
|
| 685 |
return
|
|
@@ -688,20 +709,38 @@ def _vlm_chat_core(message, image, reasoning, max_new_tokens):
|
|
| 688 |
budget = int(max_new_tokens)
|
| 689 |
_gpu_start = time.time()
|
| 690 |
|
|
|
|
| 691 |
content = []
|
| 692 |
if image is not None:
|
| 693 |
content.append({"type": "image", "image": image})
|
| 694 |
-
content.append({"type": "text", "text":
|
| 695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
|
| 697 |
-
|
| 698 |
-
messages
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
|
|
|
|
|
|
|
|
|
| 705 |
|
| 706 |
tokenizer = getattr(vlm_processor, "tokenizer", vlm_processor)
|
| 707 |
streamer = TextIteratorStreamer(
|
|
@@ -759,19 +798,20 @@ def _vlm_chat_core(message, image, reasoning, max_new_tokens):
|
|
| 759 |
|
| 760 |
|
| 761 |
@spaces.GPU
|
| 762 |
-
def vlm_chat(message, image, reasoning, max_new_tokens, progress=gr.Progress(track_tqdm=True)):
|
| 763 |
"""Answer a single user message, optionally grounded on an uploaded image.
|
| 764 |
|
| 765 |
Thin ``@spaces.GPU`` wrapper around :func:`_vlm_chat_core` (which holds the
|
| 766 |
-
token-streaming logic). ``reasoning`` ("On"/"Off") drives
|
| 767 |
``enable_thinking`` switch: Off skips the <think> trace for a direct answer
|
| 768 |
(best for prompt rewriting); On lets the model reason first (slower).
|
|
|
|
| 769 |
"""
|
| 770 |
-
yield from _vlm_chat_core(message, image, reasoning, max_new_tokens)
|
| 771 |
|
| 772 |
|
| 773 |
@spaces.GPU
|
| 774 |
-
def _image_video_check_gpu(image, message, reasoning, max_new_tokens):
|
| 775 |
"""One GPU window: moderate ``image``, then stream a video prompt from it.
|
| 776 |
|
| 777 |
Yields ``("moderation", dict)`` once the image-context check is done, then
|
|
@@ -785,7 +825,7 @@ def _image_video_check_gpu(image, message, reasoning, max_new_tokens):
|
|
| 785 |
moderation = {"ok": False, "rating": None, "confidence": None,
|
| 786 |
"flags": None, "raw": "", "error": f"{type(exc).__name__}: {exc}"}
|
| 787 |
yield ("moderation", moderation)
|
| 788 |
-
yield from _vlm_chat_core(message, image, reasoning, max_new_tokens)
|
| 789 |
|
| 790 |
|
| 791 |
def generate_and_upload(
|
|
@@ -869,16 +909,17 @@ def generate_and_upload(
|
|
| 869 |
|
| 870 |
|
| 871 |
def assistant_chat(
|
| 872 |
-
message, image, reasoning, max_new_tokens,
|
| 873 |
progress=gr.Progress(track_tqdm=True),
|
| 874 |
):
|
| 875 |
"""Gradio-facing wrapper around ``vlm_chat``.
|
| 876 |
|
| 877 |
Yields ``(answer, progress)`` so the UI streams the text live and a downstream
|
| 878 |
consumer reading the progress index sees this node advance over SSE. The final
|
| 879 |
-
``complete`` frame carries the clean answer at index 0.
|
|
|
|
| 880 |
"""
|
| 881 |
-
for ev in vlm_chat(message, image, reasoning, max_new_tokens, progress=progress):
|
| 882 |
if ev[0] == "progress":
|
| 883 |
_, produced, budget, partial = ev
|
| 884 |
frac = min(0.99, produced / max(budget, 1))
|
|
@@ -934,6 +975,7 @@ def prompt_to_video_assets(
|
|
| 934 |
randomize_seed: bool = True,
|
| 935 |
reasoning: str = "Off",
|
| 936 |
max_new_tokens: int = 512,
|
|
|
|
| 937 |
request: gr.Request = None,
|
| 938 |
) -> dict:
|
| 939 |
"""Text -> first-frame image (uploaded to R2) + video generation prompt.
|
|
@@ -977,7 +1019,7 @@ def prompt_to_video_assets(
|
|
| 977 |
frame_prompt = ""
|
| 978 |
for ev in vlm_chat(
|
| 979 |
_compose_instruction(image_instruction, original_input),
|
| 980 |
-
None, reasoning, max_new_tokens,
|
| 981 |
):
|
| 982 |
if ev[0] == "progress":
|
| 983 |
_, produced, budget, partial = ev
|
|
@@ -1044,7 +1086,7 @@ def prompt_to_video_assets(
|
|
| 1044 |
video_prompt = ""
|
| 1045 |
for ev in vlm_chat(
|
| 1046 |
_compose_instruction(video_instruction, original_input),
|
| 1047 |
-
image, reasoning, max_new_tokens,
|
| 1048 |
):
|
| 1049 |
if ev[0] == "progress":
|
| 1050 |
_, produced, budget, partial = ev
|
|
@@ -1088,6 +1130,7 @@ def image_to_video_assets(
|
|
| 1088 |
video_instruction,
|
| 1089 |
reasoning,
|
| 1090 |
max_new_tokens,
|
|
|
|
| 1091 |
progress=gr.Progress(track_tqdm=True),
|
| 1092 |
):
|
| 1093 |
"""User-uploaded image -> motion prompt + image-context (moderation) check.
|
|
@@ -1110,7 +1153,7 @@ def image_to_video_assets(
|
|
| 1110 |
video_prompt = ""
|
| 1111 |
yield video_prompt, status, _progress("check", 0.02, label="Screening image")
|
| 1112 |
|
| 1113 |
-
for ev in _image_video_check_gpu(image, instruction, reasoning, budget):
|
| 1114 |
if ev[0] == "moderation":
|
| 1115 |
status = {"moderation": ev[1]}
|
| 1116 |
yield video_prompt, status, _progress("check", 0.2, label="Image checked")
|
|
@@ -1333,9 +1376,10 @@ with gr.Blocks(fill_height=True) as demo:
|
|
| 1333 |
|
| 1334 |
with gr.Tab("💬 Prompt Assistant"):
|
| 1335 |
gr.Markdown(
|
| 1336 |
-
"Ask
|
| 1337 |
"Turn a rough idea into a rich prompt, or upload a reference image and "
|
| 1338 |
-
"ask the model to describe it as a prompt."
|
|
|
|
| 1339 |
)
|
| 1340 |
with gr.Row(equal_height=False):
|
| 1341 |
with gr.Column(scale=1, min_width=320):
|
|
@@ -1344,6 +1388,12 @@ with gr.Blocks(fill_height=True) as demo:
|
|
| 1344 |
type="pil",
|
| 1345 |
height=320,
|
| 1346 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1347 |
vlm_prompt = gr.Textbox(
|
| 1348 |
label="💬 Your Question",
|
| 1349 |
placeholder="e.g. 'Improve this prompt: a cat in a hat' — or attach an image and ask 'describe this as a prompt'",
|
|
@@ -1413,8 +1463,8 @@ with gr.Blocks(fill_height=True) as demo:
|
|
| 1413 |
outputs=[output_image, used_seed, r2_status, gen_progress],
|
| 1414 |
)
|
| 1415 |
|
| 1416 |
-
# Prompt Assistant
|
| 1417 |
-
vlm_inputs = [vlm_prompt, vlm_image, vlm_reasoning, vlm_max_tokens]
|
| 1418 |
vlm_btn.click(
|
| 1419 |
fn=assistant_chat, inputs=vlm_inputs, outputs=[vlm_output, vlm_progress],
|
| 1420 |
api_name="prompt_assistant",
|
|
@@ -1432,13 +1482,14 @@ with gr.Blocks(fill_height=True) as demo:
|
|
| 1432 |
i2v_instruction = gr.Textbox(label="i2v instruction", value="")
|
| 1433 |
i2v_reasoning = gr.Radio(choices=["Off", "On"], value="Off", label="i2v reasoning")
|
| 1434 |
i2v_max_tokens = gr.Slider(minimum=64, maximum=1024, value=256, step=64, label="i2v max tokens")
|
|
|
|
| 1435 |
i2v_btn = gr.Button("i2v", visible=False)
|
| 1436 |
i2v_prompt_out = gr.Textbox(label="i2v prompt")
|
| 1437 |
i2v_status_out = gr.JSON(label="i2v status")
|
| 1438 |
i2v_progress_out = gr.JSON(label="i2v progress")
|
| 1439 |
i2v_btn.click(
|
| 1440 |
fn=image_to_video_assets,
|
| 1441 |
-
inputs=[i2v_image, i2v_instruction, i2v_reasoning, i2v_max_tokens],
|
| 1442 |
outputs=[i2v_prompt_out, i2v_status_out, i2v_progress_out],
|
| 1443 |
api_name="image_to_video_assets",
|
| 1444 |
)
|
|
|
|
| 20 |
)
|
| 21 |
from compel import Compel, ReturnedEmbeddingsType
|
| 22 |
from huggingface_hub import hf_hub_download
|
| 23 |
+
from transformers import (
|
| 24 |
+
AutoProcessor,
|
| 25 |
+
AutoModelForImageTextToText,
|
| 26 |
+
BitsAndBytesConfig,
|
| 27 |
+
TextIteratorStreamer,
|
| 28 |
+
)
|
| 29 |
|
| 30 |
# Structured progress contract (also see wan2.2 / LTX2.3 Spaces and the generator
|
| 31 |
# orchestrator). Every streaming endpoint yields a hidden JSON "progress" output
|
|
|
|
| 150 |
# describing a reference image. Loaded once, bf16 — fits alongside the two
|
| 151 |
# diffusion pipelines on the shared GPU.
|
| 152 |
# -----------------------------------------------------------------------------
|
| 153 |
+
# Prior models, kept for easy revert (set VLM_MODEL_ID back to one of these):
|
| 154 |
# "rodrigomt/Qwen3.5-4B-Uncensored-Aggressive" # no generation_config; needed eos pinning
|
| 155 |
+
# "prithivMLmods/gemma-4-E4B-it-Uncensored-MAX" # model_type gemma4; under-rated explicit content (~2)
|
| 156 |
+
# "ccharnkij/Qwen3.5-9B-Uncensored" # 9B Qwen3.5 VL, ~18.8 GB bf16 (the previous default)
|
| 157 |
+
# Current: gemma-4-12B "unified" (text+image+audio+video), abliterated/uncensored.
|
| 158 |
+
# model_type=gemma4_unified / Gemma4UnifiedForConditionalGeneration — NEWER than the
|
| 159 |
+
# earlier gemma4, so it needs a very recent transformers (requirements pins git main).
|
| 160 |
+
# It's ~24 GB in bf16, which does NOT fit on the zero-a10g (~24 GB) next to the two
|
| 161 |
+
# diffusion pipelines, so we load it 8-bit (bitsandbytes, ~12 GB) by DEFAULT.
|
| 162 |
+
# Flip VLM_LOAD_8BIT=0 to load full bf16 (only safe on a bigger GPU). Stop tokens are
|
| 163 |
+
# handled model-agnostically by _resolve_vlm_eos_ids() (this model ships a proper
|
| 164 |
+
# generation_config + chat_template.jinja).
|
| 165 |
+
VLM_MODEL_ID = os.environ.get("VLM_MODEL_ID", "OpenYourMind/gemma-4-12B-it-abliterated-uncensored")
|
| 166 |
+
VLM_LOAD_8BIT = os.environ.get("VLM_LOAD_8BIT", "1").lower() not in ("0", "false", "no", "")
|
| 167 |
+
|
| 168 |
+
print(f"Loading assistant: {VLM_MODEL_ID} (8bit={VLM_LOAD_8BIT}) ...")
|
| 169 |
vlm_processor = AutoProcessor.from_pretrained(
|
| 170 |
VLM_MODEL_ID, token=HF_TOKEN, trust_remote_code=True
|
| 171 |
)
|
| 172 |
+
_vlm_kwargs = dict(token=HF_TOKEN, trust_remote_code=True, torch_dtype=torch.bfloat16)
|
| 173 |
+
if VLM_LOAD_8BIT:
|
| 174 |
+
# bitsandbytes quantizes the weights on GPU as they load; device_map places them
|
| 175 |
+
# there directly, so we must NOT call .to("cuda") afterwards (it errors on a
|
| 176 |
+
# quantized model).
|
| 177 |
+
_vlm_kwargs["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True)
|
| 178 |
+
_vlm_kwargs["device_map"] = "auto"
|
| 179 |
+
vlm_model = AutoModelForImageTextToText.from_pretrained(VLM_MODEL_ID, **_vlm_kwargs)
|
| 180 |
+
if not VLM_LOAD_8BIT:
|
| 181 |
+
vlm_model.to("cuda")
|
| 182 |
+
vlm_model.eval()
|
| 183 |
|
| 184 |
|
| 185 |
def _resolve_vlm_eos_ids(tokenizer, model):
|
|
|
|
| 686 |
# =============================================================================
|
| 687 |
# Prompt Assistant (Qwen3.5-4B) — single-turn chat, optional image
|
| 688 |
# =============================================================================
|
| 689 |
+
def _vlm_chat_core(message, image, reasoning, max_new_tokens, system_prompt=""):
|
| 690 |
"""Token-streaming VLM chat body (no GPU decorator).
|
| 691 |
|
| 692 |
Yields ``("progress", produced, budget, partial)`` per token and a final
|
| 693 |
``("text", answer)`` tuple. Kept decorator-free so it can be reused inside a
|
| 694 |
*single* ``@spaces.GPU`` window by other endpoints (e.g. the image→video
|
| 695 |
check), avoiding a second ZeroGPU acquisition. See :func:`vlm_chat`.
|
| 696 |
+
|
| 697 |
+
``system_prompt`` (optional) steers the assistant. Models whose chat template
|
| 698 |
+
supports a ``system`` role (e.g. Qwen) get a real system turn; templates that
|
| 699 |
+
reject one (e.g. Gemma) fall back to folding the system text into the user
|
| 700 |
+
turn, so the param works regardless of which model is loaded.
|
| 701 |
"""
|
| 702 |
message = (message or "").strip()
|
| 703 |
+
system_prompt = (system_prompt or "").strip()
|
| 704 |
if not message and image is None:
|
| 705 |
yield ("text", "Please enter a question (and optionally attach an image).")
|
| 706 |
return
|
|
|
|
| 709 |
budget = int(max_new_tokens)
|
| 710 |
_gpu_start = time.time()
|
| 711 |
|
| 712 |
+
user_text = message or "Describe this image."
|
| 713 |
content = []
|
| 714 |
if image is not None:
|
| 715 |
content.append({"type": "image", "image": image})
|
| 716 |
+
content.append({"type": "text", "text": user_text})
|
| 717 |
+
|
| 718 |
+
messages = []
|
| 719 |
+
if system_prompt:
|
| 720 |
+
messages.append({"role": "system", "content": [{"type": "text", "text": system_prompt}]})
|
| 721 |
+
messages.append({"role": "user", "content": content})
|
| 722 |
+
|
| 723 |
+
def _template(msgs):
|
| 724 |
+
return vlm_processor.apply_chat_template(
|
| 725 |
+
msgs,
|
| 726 |
+
tokenize=True,
|
| 727 |
+
add_generation_prompt=True,
|
| 728 |
+
return_dict=True,
|
| 729 |
+
return_tensors="pt",
|
| 730 |
+
enable_thinking=enable_thinking,
|
| 731 |
+
)
|
| 732 |
|
| 733 |
+
try:
|
| 734 |
+
inputs = _template(messages).to(vlm_model.device)
|
| 735 |
+
except Exception:
|
| 736 |
+
# Template has no system role (Gemma et al.) — fold it into the user turn.
|
| 737 |
+
if not system_prompt:
|
| 738 |
+
raise
|
| 739 |
+
folded = []
|
| 740 |
+
if image is not None:
|
| 741 |
+
folded.append({"type": "image", "image": image})
|
| 742 |
+
folded.append({"type": "text", "text": f"{system_prompt}\n\n{user_text}"})
|
| 743 |
+
inputs = _template([{"role": "user", "content": folded}]).to(vlm_model.device)
|
| 744 |
|
| 745 |
tokenizer = getattr(vlm_processor, "tokenizer", vlm_processor)
|
| 746 |
streamer = TextIteratorStreamer(
|
|
|
|
| 798 |
|
| 799 |
|
| 800 |
@spaces.GPU
|
| 801 |
+
def vlm_chat(message, image, reasoning, max_new_tokens, system_prompt="", progress=gr.Progress(track_tqdm=True)):
|
| 802 |
"""Answer a single user message, optionally grounded on an uploaded image.
|
| 803 |
|
| 804 |
Thin ``@spaces.GPU`` wrapper around :func:`_vlm_chat_core` (which holds the
|
| 805 |
+
token-streaming logic). ``reasoning`` ("On"/"Off") drives the model's
|
| 806 |
``enable_thinking`` switch: Off skips the <think> trace for a direct answer
|
| 807 |
(best for prompt rewriting); On lets the model reason first (slower).
|
| 808 |
+
``system_prompt`` (optional) steers the assistant.
|
| 809 |
"""
|
| 810 |
+
yield from _vlm_chat_core(message, image, reasoning, max_new_tokens, system_prompt)
|
| 811 |
|
| 812 |
|
| 813 |
@spaces.GPU
|
| 814 |
+
def _image_video_check_gpu(image, message, reasoning, max_new_tokens, system_prompt=""):
|
| 815 |
"""One GPU window: moderate ``image``, then stream a video prompt from it.
|
| 816 |
|
| 817 |
Yields ``("moderation", dict)`` once the image-context check is done, then
|
|
|
|
| 825 |
moderation = {"ok": False, "rating": None, "confidence": None,
|
| 826 |
"flags": None, "raw": "", "error": f"{type(exc).__name__}: {exc}"}
|
| 827 |
yield ("moderation", moderation)
|
| 828 |
+
yield from _vlm_chat_core(message, image, reasoning, max_new_tokens, system_prompt)
|
| 829 |
|
| 830 |
|
| 831 |
def generate_and_upload(
|
|
|
|
| 909 |
|
| 910 |
|
| 911 |
def assistant_chat(
|
| 912 |
+
message, image, reasoning, max_new_tokens, system_prompt="",
|
| 913 |
progress=gr.Progress(track_tqdm=True),
|
| 914 |
):
|
| 915 |
"""Gradio-facing wrapper around ``vlm_chat``.
|
| 916 |
|
| 917 |
Yields ``(answer, progress)`` so the UI streams the text live and a downstream
|
| 918 |
consumer reading the progress index sees this node advance over SSE. The final
|
| 919 |
+
``complete`` frame carries the clean answer at index 0. ``system_prompt``
|
| 920 |
+
(optional) steers the assistant.
|
| 921 |
"""
|
| 922 |
+
for ev in vlm_chat(message, image, reasoning, max_new_tokens, system_prompt, progress=progress):
|
| 923 |
if ev[0] == "progress":
|
| 924 |
_, produced, budget, partial = ev
|
| 925 |
frac = min(0.99, produced / max(budget, 1))
|
|
|
|
| 975 |
randomize_seed: bool = True,
|
| 976 |
reasoning: str = "Off",
|
| 977 |
max_new_tokens: int = 512,
|
| 978 |
+
system_prompt: str = "",
|
| 979 |
request: gr.Request = None,
|
| 980 |
) -> dict:
|
| 981 |
"""Text -> first-frame image (uploaded to R2) + video generation prompt.
|
|
|
|
| 1019 |
frame_prompt = ""
|
| 1020 |
for ev in vlm_chat(
|
| 1021 |
_compose_instruction(image_instruction, original_input),
|
| 1022 |
+
None, reasoning, max_new_tokens, system_prompt,
|
| 1023 |
):
|
| 1024 |
if ev[0] == "progress":
|
| 1025 |
_, produced, budget, partial = ev
|
|
|
|
| 1086 |
video_prompt = ""
|
| 1087 |
for ev in vlm_chat(
|
| 1088 |
_compose_instruction(video_instruction, original_input),
|
| 1089 |
+
image, reasoning, max_new_tokens, system_prompt,
|
| 1090 |
):
|
| 1091 |
if ev[0] == "progress":
|
| 1092 |
_, produced, budget, partial = ev
|
|
|
|
| 1130 |
video_instruction,
|
| 1131 |
reasoning,
|
| 1132 |
max_new_tokens,
|
| 1133 |
+
system_prompt="",
|
| 1134 |
progress=gr.Progress(track_tqdm=True),
|
| 1135 |
):
|
| 1136 |
"""User-uploaded image -> motion prompt + image-context (moderation) check.
|
|
|
|
| 1153 |
video_prompt = ""
|
| 1154 |
yield video_prompt, status, _progress("check", 0.02, label="Screening image")
|
| 1155 |
|
| 1156 |
+
for ev in _image_video_check_gpu(image, instruction, reasoning, budget, system_prompt):
|
| 1157 |
if ev[0] == "moderation":
|
| 1158 |
status = {"moderation": ev[1]}
|
| 1159 |
yield video_prompt, status, _progress("check", 0.2, label="Image checked")
|
|
|
|
| 1376 |
|
| 1377 |
with gr.Tab("💬 Prompt Assistant"):
|
| 1378 |
gr.Markdown(
|
| 1379 |
+
"Ask the assistant model a single question — with or without an image. "
|
| 1380 |
"Turn a rough idea into a rich prompt, or upload a reference image and "
|
| 1381 |
+
"ask the model to describe it as a prompt. Use the **System Prompt** to "
|
| 1382 |
+
"steer its tone/role."
|
| 1383 |
)
|
| 1384 |
with gr.Row(equal_height=False):
|
| 1385 |
with gr.Column(scale=1, min_width=320):
|
|
|
|
| 1388 |
type="pil",
|
| 1389 |
height=320,
|
| 1390 |
)
|
| 1391 |
+
vlm_system = gr.Textbox(
|
| 1392 |
+
label="🧭 System Prompt (optional)",
|
| 1393 |
+
placeholder="e.g. 'You are an expert Danbooru tag writer. Output only comma-separated tags.'",
|
| 1394 |
+
lines=2,
|
| 1395 |
+
max_lines=8,
|
| 1396 |
+
)
|
| 1397 |
vlm_prompt = gr.Textbox(
|
| 1398 |
label="💬 Your Question",
|
| 1399 |
placeholder="e.g. 'Improve this prompt: a cat in a hat' — or attach an image and ask 'describe this as a prompt'",
|
|
|
|
| 1463 |
outputs=[output_image, used_seed, r2_status, gen_progress],
|
| 1464 |
)
|
| 1465 |
|
| 1466 |
+
# Prompt Assistant — single-turn, optional image, optional system prompt
|
| 1467 |
+
vlm_inputs = [vlm_prompt, vlm_image, vlm_reasoning, vlm_max_tokens, vlm_system]
|
| 1468 |
vlm_btn.click(
|
| 1469 |
fn=assistant_chat, inputs=vlm_inputs, outputs=[vlm_output, vlm_progress],
|
| 1470 |
api_name="prompt_assistant",
|
|
|
|
| 1482 |
i2v_instruction = gr.Textbox(label="i2v instruction", value="")
|
| 1483 |
i2v_reasoning = gr.Radio(choices=["Off", "On"], value="Off", label="i2v reasoning")
|
| 1484 |
i2v_max_tokens = gr.Slider(minimum=64, maximum=1024, value=256, step=64, label="i2v max tokens")
|
| 1485 |
+
i2v_system = gr.Textbox(label="i2v system prompt", value="")
|
| 1486 |
i2v_btn = gr.Button("i2v", visible=False)
|
| 1487 |
i2v_prompt_out = gr.Textbox(label="i2v prompt")
|
| 1488 |
i2v_status_out = gr.JSON(label="i2v status")
|
| 1489 |
i2v_progress_out = gr.JSON(label="i2v progress")
|
| 1490 |
i2v_btn.click(
|
| 1491 |
fn=image_to_video_assets,
|
| 1492 |
+
inputs=[i2v_image, i2v_instruction, i2v_reasoning, i2v_max_tokens, i2v_system],
|
| 1493 |
outputs=[i2v_prompt_out, i2v_status_out, i2v_progress_out],
|
| 1494 |
api_name="image_to_video_assets",
|
| 1495 |
)
|
requirements.txt
CHANGED
|
@@ -1,8 +1,10 @@
|
|
| 1 |
gradio[mcp]
|
| 2 |
git+https://github.com/huggingface/diffusers
|
| 3 |
-
transformers
|
|
|
|
| 4 |
torchvision
|
| 5 |
accelerate
|
|
|
|
| 6 |
compel
|
| 7 |
sentencepiece
|
| 8 |
numpy
|
|
|
|
| 1 |
gradio[mcp]
|
| 2 |
git+https://github.com/huggingface/diffusers
|
| 3 |
+
# gemma-4-12B is model_type gemma4_unified — needs a very recent transformers.
|
| 4 |
+
git+https://github.com/huggingface/transformers
|
| 5 |
torchvision
|
| 6 |
accelerate
|
| 7 |
+
bitsandbytes
|
| 8 |
compel
|
| 9 |
sentencepiece
|
| 10 |
numpy
|