Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,187 +1,74 @@
|
|
| 1 |
-
import
|
| 2 |
import gradio as gr
|
| 3 |
-
import torch
|
| 4 |
from PIL import Image
|
| 5 |
-
from transformers import AutoModel, AutoTokenizer
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
|
| 12 |
-
|
| 13 |
-
tokenizer = None
|
| 14 |
-
|
| 15 |
-
def load_model():
|
| 16 |
-
global model, tokenizer
|
| 17 |
-
if model is None or tokenizer is None:
|
| 18 |
-
model = AutoModel.from_pretrained(
|
| 19 |
-
MODEL_ID,
|
| 20 |
-
trust_remote_code=True,
|
| 21 |
-
attn_implementation="sdpa",
|
| 22 |
-
torch_dtype=DTYPE,
|
| 23 |
-
)
|
| 24 |
-
if DEVICE == "cuda":
|
| 25 |
-
model = model.to(DEVICE)
|
| 26 |
-
model = model.eval()
|
| 27 |
-
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
| 28 |
-
return model, tokenizer
|
| 29 |
-
|
| 30 |
-
# --- BUG FIX & REFACTOR: Replaced format_history_for_model ---
|
| 31 |
-
# The original function was complex and not well-suited for a stateful, turn-by-turn
|
| 32 |
-
# chatbot where images are part of the history. This new function is clearer and more robust.
|
| 33 |
-
def convert_history_to_model_messages(history: list, new_message: str, image: Image.Image):
|
| 34 |
-
"""
|
| 35 |
-
Converts Gradio chatbot history and the current turn's input into the MiniCPM message format.
|
| 36 |
-
- history: Gradio chatbot history list. Each item is a tuple (user_turn, assistant_turn).
|
| 37 |
-
A user_turn can be a string or a tuple (image_pil, text).
|
| 38 |
-
- new_message: The text from the user in the current turn.
|
| 39 |
-
- image: The PIL image uploaded by the user in the current turn.
|
| 40 |
-
"""
|
| 41 |
-
messages = []
|
| 42 |
-
# Process past turns
|
| 43 |
-
for user_turn, assistant_turn in history:
|
| 44 |
-
# Handle user message
|
| 45 |
-
if isinstance(user_turn, tuple):
|
| 46 |
-
# This turn had an image
|
| 47 |
-
img, text = user_turn
|
| 48 |
-
# MiniCPM expects content as a list of parts (image, text)
|
| 49 |
-
messages.append({"role": "user", "content": [img, text]})
|
| 50 |
-
else:
|
| 51 |
-
# This turn was text-only
|
| 52 |
-
messages.append({"role": "user", "content": [user_turn]})
|
| 53 |
-
|
| 54 |
-
# Handle assistant message if it exists
|
| 55 |
-
if assistant_turn:
|
| 56 |
-
messages.append({"role": "assistant", "content": [assistant_turn]})
|
| 57 |
-
|
| 58 |
-
# Process the current turn's input
|
| 59 |
-
current_turn_content = []
|
| 60 |
-
if image:
|
| 61 |
-
current_turn_content.append(image)
|
| 62 |
-
if new_message and new_message.strip():
|
| 63 |
-
current_turn_content.append(new_message)
|
| 64 |
-
|
| 65 |
-
# Add the current user message to the list if it's not empty
|
| 66 |
-
if current_turn_content:
|
| 67 |
-
messages.append({"role": "user", "content": current_turn_content})
|
| 68 |
-
|
| 69 |
-
return messages
|
| 70 |
-
|
| 71 |
-
def stream_chat(messages, enable_thinking=False):
|
| 72 |
"""
|
| 73 |
-
|
|
|
|
|
|
|
| 74 |
"""
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
)
|
| 82 |
-
buffer = ""
|
| 83 |
-
for chunk in answer_iter:
|
| 84 |
-
buffer += chunk
|
| 85 |
-
yield buffer
|
| 86 |
-
|
| 87 |
-
def chat_and_stream(message: str, history: list, image: Image.Image, enable_thinking: bool):
|
| 88 |
-
"""
|
| 89 |
-
The main event handler for the chatbot.
|
| 90 |
-
- Takes current inputs and history.
|
| 91 |
-
- Updates the chatbot UI instantly with the user's message.
|
| 92 |
-
- Converts history to the model's format.
|
| 93 |
-
- Streams the model's response back to the chatbot.
|
| 94 |
-
"""
|
| 95 |
-
# Guard against empty submissions
|
| 96 |
-
if not image and (not message or not message.strip()):
|
| 97 |
-
# Return original history and unchanged inputs
|
| 98 |
-
yield history, "", image
|
| 99 |
-
return
|
| 100 |
-
|
| 101 |
-
# Prepare user message for display in the chatbot
|
| 102 |
-
# If an image is present, group it with the message text in a tuple for display
|
| 103 |
-
user_display_turn = (image, message) if image else message
|
| 104 |
-
history.append([user_display_turn, None])
|
| 105 |
-
# Instantly update the UI: show user message, clear inputs
|
| 106 |
-
yield history, "", None
|
| 107 |
-
|
| 108 |
-
# Convert the history (including the new user turn) for the model
|
| 109 |
-
model_messages = convert_history_to_model_messages(history, "", None) # History already contains the new turn
|
| 110 |
|
| 111 |
-
# Stream the response from the model
|
| 112 |
-
full_response = ""
|
| 113 |
-
for partial_response in stream_chat(model_messages, enable_thinking=enable_thinking):
|
| 114 |
-
history[-1][1] = partial_response
|
| 115 |
-
full_response = partial_response
|
| 116 |
-
yield history, "", None
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
# --- UI REFACTOR: Replaced gr.ChatInterface with gr.Blocks layout ---
|
| 120 |
-
with gr.Blocks(fill_height=True, analytics_enabled=False, theme=gr.themes.Default(text_size=gr.themes.sizes.text_lg)) as demo:
|
| 121 |
-
gr.Markdown("# MiniCPM-V-4_5 Visual Chat Demo")
|
| 122 |
-
|
| 123 |
with gr.Row():
|
| 124 |
-
# Left Column: Inputs
|
| 125 |
with gr.Column(scale=1):
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
| 127 |
text_in = gr.Textbox(
|
| 128 |
-
label="
|
| 129 |
-
placeholder="
|
| 130 |
-
lines=
|
| 131 |
-
|
| 132 |
-
)
|
| 133 |
-
with gr.Row():
|
| 134 |
-
submit_btn = gr.Button("Submit", variant="primary", scale=3)
|
| 135 |
-
clear_btn = gr.Button("Clear", scale=1)
|
| 136 |
-
|
| 137 |
-
with gr.Accordion("Advanced Options", open=False):
|
| 138 |
-
enable_thinking_box = gr.Checkbox(label="Enable Thinking Mode", value=False)
|
| 139 |
-
|
| 140 |
-
with gr.Group():
|
| 141 |
-
gr.Markdown("### Model Info")
|
| 142 |
-
gr.Textbox(value=MODEL_ID, label="Model", interactive=False)
|
| 143 |
-
gr.Textbox(value=DEVICE, label="Device", interactive=False)
|
| 144 |
-
gr.Textbox(value=str(DTYPE), label="DType", interactive=False)
|
| 145 |
-
|
| 146 |
-
# Right Column: Chatbot Output
|
| 147 |
-
with gr.Column(scale=2):
|
| 148 |
-
chatbot = gr.Chatbot(
|
| 149 |
-
label="MiniCPM Chat",
|
| 150 |
-
bubble_full_width=False,
|
| 151 |
-
height=700, # Increased height for better viewing
|
| 152 |
-
render_markdown=True,
|
| 153 |
-
likeable=False,
|
| 154 |
-
show_copy_button=True,
|
| 155 |
)
|
|
|
|
|
|
|
| 156 |
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
|
| 163 |
-
# Click event for the submit button
|
| 164 |
submit_btn.click(
|
| 165 |
-
fn=
|
| 166 |
-
inputs=
|
| 167 |
-
outputs=
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
# Submit on Enter key press in the textbox
|
| 171 |
-
text_in.submit(
|
| 172 |
-
fn=chat_and_stream,
|
| 173 |
-
inputs=inputs,
|
| 174 |
-
outputs=outputs
|
| 175 |
)
|
| 176 |
-
|
| 177 |
-
# Clear button functionality
|
| 178 |
-
def clear_all():
|
| 179 |
-
return [], "", None # Clears chatbot, textbox, and image
|
| 180 |
-
clear_btn.click(fn=clear_all, outputs=[chatbot, text_in, image_in])
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
# Preload model on app start for snappier first response
|
| 184 |
-
demo.load(load_model, outputs=None, queue=False)
|
| 185 |
|
| 186 |
-
|
| 187 |
-
demo.
|
|
|
|
| 1 |
+
import time
|
| 2 |
import gradio as gr
|
|
|
|
| 3 |
from PIL import Image
|
|
|
|
| 4 |
|
| 5 |
+
# Added more placeholder responses to prevent an IndexError
|
| 6 |
+
PREWRITTEN_RESPONSES = [
|
| 7 |
+
"When it comes to retailing industry, we offer remind the both part of realistic store and internet shopping. Both of them are all have their pros and cons, but according the picture, we can find out both of the internet sales counting and its profit are all grew up every years between twenty eighteen to twenty twenty one. The years increase rate began with twenty eighteen only 10.3%, next year 14.1%, and the next 20.3%, finally finished in twenty twenty one up to 24.5%. The sales profit also began with twenty eighteen only 2517 (million), next year 2873, and the next 3456, finally finished in twenty twenty one up to 4303. Therefore, we can find out the internet shopping is grew up between the four years. Begin 2019, according my observed more and more friends change to internet shopping because of COVID-19. All above the results provided the picture is to the realistic.\nIn my opinion, shopping on the internet can save many times to me, so I also do it when I"
|
| 8 |
+
]
|
| 9 |
|
| 10 |
+
def fake_minicpm_infer(image: Image.Image, text: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
"""
|
| 12 |
+
Simulate a MiniCPM-V-4_5 inference:
|
| 13 |
+
- Sleep for a fixed duration to mimic model loading & generation latency.
|
| 14 |
+
- Return a prewritten response based on simple heuristics of input.
|
| 15 |
"""
|
| 16 |
+
if image is None and not text.strip():
|
| 17 |
+
return "Please provide an image or text to start the demo."
|
| 18 |
+
|
| 19 |
+
time.sleep(8.5) # Simulate inference time
|
| 20 |
+
|
| 21 |
+
t = text.lower().strip()
|
| 22 |
+
if any(k in t for k in ["travel", "advice", "safety", "suggestion"]):
|
| 23 |
+
return PREWRITTEN_RESPONSES[1]
|
| 24 |
+
if any(k in t for k in ["weather", "rain", "wind", "cloud"]):
|
| 25 |
+
return PREWRITTEN_RESPONSES[2]
|
| 26 |
+
if any(k in t for k in ["photography", "camera", "photo", "shoot"]):
|
| 27 |
+
return PREWRITTEN_RESPONSES[3]
|
| 28 |
+
return PREWRITTEN_RESPONSES[0]
|
| 29 |
+
|
| 30 |
+
custom_css = """
|
| 31 |
+
#input_textbox textarea,
|
| 32 |
+
#output_textbox textarea {
|
| 33 |
+
font-size: 18px !important;
|
| 34 |
+
}
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
with gr.Blocks(title="MiniCPM-V-4_5 Demo", css=custom_css) as demo:
|
| 38 |
+
|
| 39 |
+
gr.Markdown(
|
| 40 |
+
"""
|
| 41 |
+
# MiniCPM-V-4_5 Demo
|
| 42 |
+
"""
|
| 43 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
with gr.Row():
|
|
|
|
| 46 |
with gr.Column(scale=1):
|
| 47 |
+
# --- MODIFICATION 1 ---
|
| 48 |
+
# Set the maximum display height of the image component to 800px.
|
| 49 |
+
image_in = gr.Image(label="Input Image", type="pil", height=800)
|
| 50 |
+
|
| 51 |
text_in = gr.Textbox(
|
| 52 |
+
label="Input Question/Description",
|
| 53 |
+
placeholder="e.g., What kind of landscape is this? or What should I be aware of when traveling?",
|
| 54 |
+
lines=3,
|
| 55 |
+
elem_id="input_textbox"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
)
|
| 57 |
+
|
| 58 |
+
submit_btn = gr.Button("Submit", variant="primary")
|
| 59 |
|
| 60 |
+
with gr.Column(scale=1):
|
| 61 |
+
gr.Markdown("### Output")
|
| 62 |
+
# --- MODIFICATION 2 ---
|
| 63 |
+
# Increased the number of lines to 12 (original 8 * 1.5).
|
| 64 |
+
output = gr.Textbox(label="Model Response", lines=12, elem_id="output_textbox")
|
| 65 |
|
|
|
|
| 66 |
submit_btn.click(
|
| 67 |
+
fn=fake_minicpm_infer,
|
| 68 |
+
inputs=[image_in, text_in],
|
| 69 |
+
outputs=output,
|
| 70 |
+
api_name="mock_infer"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
+
if __name__ == "__main__":
|
| 74 |
+
demo.launch()
|