File size: 2,630 Bytes
5e288ca
 
 
79b56f3
 
5d245cc
5e288ca
 
e39cfc2
79b56f3
02730dd
 
79b56f3
 
 
a4327db
79b56f3
 
5e288ca
 
79b56f3
5e288ca
a82ce96
5e288ca
4cde538
5e288ca
bed9a72
5e288ca
 
 
4cde538
5e288ca
4cde538
02730dd
5e288ca
02730dd
5e288ca
4cde538
 
 
 
 
 
5e288ca
 
02730dd
79b56f3
4cde538
79b56f3
 
 
 
4cde538
 
 
 
 
02730dd
5e288ca
3d0c68f
 
5d245cc
 
 
 
 
3d0c68f
5d245cc
 
 
 
 
 
 
00d07aa
5d245cc
02730dd
 
e39cfc2
5e288ca
 
 
 
 
79b56f3
5e288ca
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import os
from threading import Thread

import spaces
import torch
from transformers import AutoProcessor, AutoModelForMultimodalLM, TextIteratorStreamer
from gradio import Server
from gradio.data_classes import FileData
from fastapi.responses import HTMLResponse

MODEL_ID = "meta-models/Muse-Glimmer-30B"

processor = AutoProcessor.from_pretrained(MODEL_ID)
model = AutoModelForMultimodalLM.from_pretrained(
    MODEL_ID,
    dtype=torch.bfloat16,
).to("cuda")

app = Server()


@app.api(name="chat")
@spaces.GPU(size="xlarge", duration=240)
def chat(message: str,
         media: FileData | None,
         history: list,
         reasoning: str) -> str:
    """Streaming multimodal chat with Muse-Glimmer-30B.

    history: list of {"role": "user"|"assistant", "content": str}
    media: optional image or video FileData uploaded via the Gradio client
    """
    messages = []
    for m in history:
        messages.append({"role": m["role"], "content": m["content"]})

    content = []
    if media:
        path = media["path"]
        if path.lower().rsplit(".", 1)[-1] in ("mp4", "mov", "webm", "mkv", "avi"):
            content.append({"type": "video", "path": path})
        else:
            content.append({"type": "image", "path": path})
    if message:
        content.append({"type": "text", "text": message})
    messages.append({"role": "user", "content": content})

    template_kwargs = dict(
        add_generation_prompt=True,
        tokenize=True,
        return_dict=True,
        return_tensors="pt",
        reasoning_strength=reasoning,
    )
    if any(c["type"] == "video" for c in content):
        template_kwargs["processor_kwargs"] = {"num_frames": 32}
    inputs = processor.apply_chat_template(messages, **template_kwargs).to(model.device)

    streamer = TextIteratorStreamer(processor, skip_prompt=True, skip_special_tokens=True)
    # Use the model's generation_config defaults (recommended sampling
    # params ship with the model); only cap length for ZeroGPU runtime.
    thread = Thread(
        target=model.generate,
        kwargs=dict(
            **inputs,
            streamer=streamer,
            max_new_tokens=1024,
        ),
    )
    thread.start()

    reply = ""
    for token in streamer:
        reply += token
        yield reply  # raw stream; frontend splits reasoning from final reply
    thread.join()


@app.get("/", response_class=HTMLResponse)
async def homepage():
    html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
    with open(html_path, "r", encoding="utf-8") as f:
        return f.read()


app.launch(show_error=True)