Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -1,237 +1,232 @@
|
|
1 |
-
|
|
|
|
|
|
|
2 |
import os
|
3 |
-
import spaces
|
4 |
-
from transformers import GemmaTokenizer, AutoModelForCausalLM
|
5 |
-
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
|
6 |
-
from threading import Thread
|
7 |
-
|
8 |
-
# Set an environment variable
|
9 |
-
HF_TOKEN = os.environ.get("HF_TOKEN", None)
|
10 |
-
|
11 |
-
|
12 |
-
DESCRIPTION = '''
|
13 |
-
<div>
|
14 |
-
<h1 style="text-align: center;">LLaMA-Mesh</h1>
|
15 |
-
<div>
|
16 |
-
<a style="display:inline-block" href="https://research.nvidia.com/labs/toronto-ai/LLaMA-Mesh/"><img src='https://img.shields.io/badge/public_website-8A2BE2'></a>
|
17 |
-
<a style="display:inline-block; margin-left: .5em" href="https://github.com/nv-tlabs/LLaMA-Mesh"><img src='https://img.shields.io/github/stars/nv-tlabs/LLaMA-Mesh?style=social'/></a>
|
18 |
-
</div>
|
19 |
-
<p>LLaMA-Mesh: Unifying 3D Mesh Generation with Language Models.<a style="display:inline-block" href="https://research.nvidia.com/labs/toronto-ai/LLaMA-Mesh/">[Project Page]</a> <a style="display:inline-block" href="https://github.com/nv-tlabs/LLaMA-Mesh">[Code]</a></p>
|
20 |
-
<p> Notice: (1) This demo supports up to 4096 tokens due to computational limits, while our full model supports 8k tokens. This limitation may result in incomplete generated meshes. To experience the full 8k token context, please run our model locally.</p>
|
21 |
-
<p>(2) We only support generating a single mesh per dialog round. To generate another mesh, click the "clear" button and start a new dialog.</p>
|
22 |
-
<p>(3) If the LLM refuses to generate a 3D mesh, try adding more explicit instructions to the prompt, such as "create a 3D model of a table <strong>in OBJ format</strong>." A more effective approach is to request the mesh generation at the start of the dialog.</p>
|
23 |
-
</div>
|
24 |
-
'''
|
25 |
-
|
26 |
-
LICENSE = """
|
27 |
-
<p/>
|
28 |
-
---
|
29 |
-
Built with Meta Llama 3.1 8B
|
30 |
-
"""
|
31 |
-
|
32 |
-
PLACEHOLDER = """
|
33 |
-
<div style="padding: 30px; text-align: center; display: flex; flex-direction: column; align-items: center;">
|
34 |
-
<h1 style="font-size: 28px; margin-bottom: 2px; opacity: 0.55;">LLaMA-Mesh</h1>
|
35 |
-
<p style="font-size: 18px; margin-bottom: 2px; opacity: 0.65;">Create 3D meshes by chatting.</p>
|
36 |
-
</div>
|
37 |
-
"""
|
38 |
-
|
39 |
-
|
40 |
-
css = """
|
41 |
-
h1 {
|
42 |
-
text-align: center;
|
43 |
-
display: block;
|
44 |
-
}
|
45 |
-
#duplicate-button {
|
46 |
-
margin: auto;
|
47 |
-
color: white;
|
48 |
-
background: #1565c0;
|
49 |
-
border-radius: 100vh;
|
50 |
-
}
|
51 |
-
"""
|
52 |
-
# Load the tokenizer and model
|
53 |
-
model_path = "Zhengyi/LLaMA-Mesh"
|
54 |
-
tokenizer = AutoTokenizer.from_pretrained(model_path)
|
55 |
-
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto")
|
56 |
-
terminators = [
|
57 |
-
tokenizer.eos_token_id,
|
58 |
-
tokenizer.convert_tokens_to_ids("<|eot_id|>")
|
59 |
-
]
|
60 |
-
|
61 |
-
|
62 |
-
from trimesh.exchange.gltf import export_glb
|
63 |
-
import gradio as gr
|
64 |
-
import trimesh
|
65 |
import numpy as np
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
import tempfile
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
generate_kwargs = dict(
|
143 |
-
input_ids= input_ids,
|
144 |
-
streamer=streamer,
|
145 |
-
max_new_tokens=max_new_tokens,
|
146 |
-
do_sample=True,
|
147 |
-
temperature=temperature,
|
148 |
-
eos_token_id=terminators,
|
149 |
)
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
171 |
with gr.Row():
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
['Write a python code for sorting.'],
|
202 |
-
['How to setup a human base on Mars? Give short answer.'],
|
203 |
-
['Explain theory of relativity to me like I’m 8 years old.'],
|
204 |
-
['What is 9,000 * 9,000?'],
|
205 |
-
['Create a 3D model of a soda can.'],
|
206 |
-
['Create a 3D model of a sword.'],
|
207 |
-
['Create a 3D model of a wooden barrel'],
|
208 |
-
['Create a 3D model of a chair.']
|
209 |
-
],
|
210 |
-
cache_examples=False,
|
211 |
-
)
|
212 |
-
gr.Markdown(LICENSE)
|
213 |
-
|
214 |
-
with gr.Column(scale=2):
|
215 |
-
output_model = gr.Model3D(
|
216 |
-
label="3D Mesh Visualization",
|
217 |
-
interactive=False,
|
218 |
-
)
|
219 |
-
gr.Markdown("You can copy the generated 3d objects in the left and paste in the textbox below. Put the button and you will see the visualization of the 3D mesh.")
|
220 |
-
|
221 |
-
# Add the text box for 3D mesh input and button
|
222 |
-
mesh_input = gr.Textbox(
|
223 |
-
label="3D Mesh Input",
|
224 |
-
placeholder="Paste your 3D mesh in OBJ format here...",
|
225 |
-
lines=5,
|
226 |
-
)
|
227 |
-
visualize_button = gr.Button("Visualize 3D Mesh")
|
228 |
-
|
229 |
-
# Link the button to the visualization function
|
230 |
-
visualize_button.click(
|
231 |
-
fn=apply_gradient_color,
|
232 |
-
inputs=[mesh_input],
|
233 |
-
outputs=[output_model]
|
234 |
-
)
|
235 |
-
|
236 |
-
if __name__ == "__main__":
|
237 |
-
demo.launch()
|
|
|
1 |
+
# Copyright (c) Alibaba Cloud.
|
2 |
+
#
|
3 |
+
# This source code is licensed under the license found in the
|
4 |
+
# LICENSE file in the root directory of this source tree.
|
5 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
import numpy as np
|
7 |
+
from urllib3.exceptions import HTTPError
|
8 |
+
os.system('pip install dashscope modelscope oss2 -U')
|
9 |
+
|
10 |
+
from argparse import ArgumentParser
|
11 |
+
from pathlib import Path
|
12 |
+
|
13 |
+
import copy
|
14 |
+
import gradio as gr
|
15 |
+
import oss2
|
16 |
+
import os
|
17 |
+
import re
|
18 |
+
import secrets
|
19 |
import tempfile
|
20 |
+
import requests
|
21 |
+
from http import HTTPStatus
|
22 |
+
from dashscope import MultiModalConversation
|
23 |
+
import dashscope
|
24 |
+
|
25 |
+
API_KEY = os.environ['API_KEY']
|
26 |
+
dashscope.api_key = API_KEY
|
27 |
+
|
28 |
+
REVISION = 'v1.0.4'
|
29 |
+
BOX_TAG_PATTERN = r"<box>([\s\S]*?)</box>"
|
30 |
+
PUNCTUATION = "!?。"#$%&'()*+,-/:;<=>@[\]^_`{|}~⦅⦆「」、、〃》「」『』【】〔〕〖〗〘〙〚〛〜〝〞〟〰〾〿–—‘’‛“”„‟…‧﹏."
|
31 |
+
|
32 |
+
|
33 |
+
def _get_args():
|
34 |
+
parser = ArgumentParser()
|
35 |
+
parser.add_argument("--revision", type=str, default=REVISION)
|
36 |
+
parser.add_argument("--cpu-only", action="store_true", help="Run demo with CPU only")
|
37 |
+
|
38 |
+
parser.add_argument("--share", action="store_true", default=False,
|
39 |
+
help="Create a publicly shareable link for the interface.")
|
40 |
+
parser.add_argument("--inbrowser", action="store_true", default=False,
|
41 |
+
help="Automatically launch the interface in a new tab on the default browser.")
|
42 |
+
parser.add_argument("--server-port", type=int, default=7860,
|
43 |
+
help="Demo server port.")
|
44 |
+
parser.add_argument("--server-name", type=str, default="127.0.0.1",
|
45 |
+
help="Demo server name.")
|
46 |
+
|
47 |
+
args = parser.parse_args()
|
48 |
+
return args
|
49 |
+
|
50 |
+
def _parse_text(text):
|
51 |
+
lines = text.split("\n")
|
52 |
+
lines = [line for line in lines if line != ""]
|
53 |
+
count = 0
|
54 |
+
for i, line in enumerate(lines):
|
55 |
+
if "```" in line:
|
56 |
+
count += 1
|
57 |
+
items = line.split("`")
|
58 |
+
if count % 2 == 1:
|
59 |
+
lines[i] = f'<pre><code class="language-{items[-1]}">'
|
60 |
+
else:
|
61 |
+
lines[i] = f"<br></code></pre>"
|
62 |
+
else:
|
63 |
+
if i > 0:
|
64 |
+
if count % 2 == 1:
|
65 |
+
line = line.replace("`", r"\`")
|
66 |
+
line = line.replace("<", "<")
|
67 |
+
line = line.replace(">", ">")
|
68 |
+
line = line.replace(" ", " ")
|
69 |
+
line = line.replace("*", "*")
|
70 |
+
line = line.replace("_", "_")
|
71 |
+
line = line.replace("-", "-")
|
72 |
+
line = line.replace(".", ".")
|
73 |
+
line = line.replace("!", "!")
|
74 |
+
line = line.replace("(", "(")
|
75 |
+
line = line.replace(")", ")")
|
76 |
+
line = line.replace("$", "$")
|
77 |
+
lines[i] = "<br>" + line
|
78 |
+
text = "".join(lines)
|
79 |
+
return text
|
80 |
+
|
81 |
+
|
82 |
+
def _remove_image_special(text):
|
83 |
+
text = text.replace('<ref>', '').replace('</ref>', '')
|
84 |
+
return re.sub(r'<box>.*?(</box>|$)', '', text)
|
85 |
+
|
86 |
+
|
87 |
+
def is_video_file(filename):
|
88 |
+
video_extensions = ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm', '.mpeg']
|
89 |
+
return any(filename.lower().endswith(ext) for ext in video_extensions)
|
90 |
+
|
91 |
+
|
92 |
+
def _launch_demo(args):
|
93 |
+
uploaded_file_dir = os.environ.get("GRADIO_TEMP_DIR") or str(
|
94 |
+
Path(tempfile.gettempdir()) / "gradio"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
)
|
96 |
+
|
97 |
+
def predict(_chatbot, task_history):
|
98 |
+
chat_query = _chatbot[-1][0]
|
99 |
+
query = task_history[-1][0]
|
100 |
+
if len(chat_query) == 0:
|
101 |
+
_chatbot.pop()
|
102 |
+
task_history.pop()
|
103 |
+
return _chatbot
|
104 |
+
print("User: " + _parse_text(query))
|
105 |
+
history_cp = copy.deepcopy(task_history)
|
106 |
+
full_response = ""
|
107 |
+
messages = []
|
108 |
+
content = []
|
109 |
+
for q, a in history_cp:
|
110 |
+
if isinstance(q, (tuple, list)):
|
111 |
+
if is_video_file(q[0]):
|
112 |
+
content.append({'video': f'file://{q[0]}'})
|
113 |
+
else:
|
114 |
+
content.append({'image': f'file://{q[0]}'})
|
115 |
+
else:
|
116 |
+
content.append({'text': q})
|
117 |
+
messages.append({'role': 'user', 'content': content})
|
118 |
+
messages.append({'role': 'assistant', 'content': [{'text': a}]})
|
119 |
+
content = []
|
120 |
+
messages.pop()
|
121 |
+
responses = MultiModalConversation.call(
|
122 |
+
model='qwen2.5-vl-32b-instruct', messages=messages, stream=True,
|
123 |
+
)
|
124 |
+
for response in responses:
|
125 |
+
if not response.status_code == HTTPStatus.OK:
|
126 |
+
raise HTTPError(f'response.code: {response.code}\nresponse.message: {response.message}')
|
127 |
+
response = response.output.choices[0].message.content
|
128 |
+
response_text = []
|
129 |
+
for ele in response:
|
130 |
+
if 'text' in ele:
|
131 |
+
response_text.append(ele['text'])
|
132 |
+
elif 'box' in ele:
|
133 |
+
response_text.append(ele['box'])
|
134 |
+
response_text = ''.join(response_text)
|
135 |
+
_chatbot[-1] = (_parse_text(chat_query), _remove_image_special(response_text))
|
136 |
+
yield _chatbot
|
137 |
+
|
138 |
+
if len(response) > 1:
|
139 |
+
result_image = response[-1]['result_image']
|
140 |
+
resp = requests.get(result_image)
|
141 |
+
os.makedirs(uploaded_file_dir, exist_ok=True)
|
142 |
+
name = f"tmp{secrets.token_hex(20)}.jpg"
|
143 |
+
filename = os.path.join(uploaded_file_dir, name)
|
144 |
+
with open(filename, 'wb') as f:
|
145 |
+
f.write(resp.content)
|
146 |
+
response = ''.join(r['box'] if 'box' in r else r['text'] for r in response[:-1])
|
147 |
+
_chatbot.append((None, (filename,)))
|
148 |
+
else:
|
149 |
+
response = response[0]['text']
|
150 |
+
_chatbot[-1] = (_parse_text(chat_query), response)
|
151 |
+
full_response = _parse_text(response)
|
152 |
+
|
153 |
+
task_history[-1] = (query, full_response)
|
154 |
+
print("Qwen2.5-VL-Chat: " + _parse_text(full_response))
|
155 |
+
yield _chatbot
|
156 |
+
|
157 |
+
|
158 |
+
def regenerate(_chatbot, task_history):
|
159 |
+
if not task_history:
|
160 |
+
return _chatbot
|
161 |
+
item = task_history[-1]
|
162 |
+
if item[1] is None:
|
163 |
+
return _chatbot
|
164 |
+
task_history[-1] = (item[0], None)
|
165 |
+
chatbot_item = _chatbot.pop(-1)
|
166 |
+
if chatbot_item[0] is None:
|
167 |
+
_chatbot[-1] = (_chatbot[-1][0], None)
|
168 |
+
else:
|
169 |
+
_chatbot.append((chatbot_item[0], None))
|
170 |
+
_chatbot_gen = predict(_chatbot, task_history)
|
171 |
+
for _chatbot in _chatbot_gen:
|
172 |
+
yield _chatbot
|
173 |
+
|
174 |
+
def add_text(history, task_history, text):
|
175 |
+
task_text = text
|
176 |
+
history = history if history is not None else []
|
177 |
+
task_history = task_history if task_history is not None else []
|
178 |
+
history = history + [(_parse_text(text), None)]
|
179 |
+
task_history = task_history + [(task_text, None)]
|
180 |
+
return history, task_history, ""
|
181 |
+
|
182 |
+
def add_file(history, task_history, file):
|
183 |
+
history = history if history is not None else []
|
184 |
+
task_history = task_history if task_history is not None else []
|
185 |
+
history = history + [((file.name,), None)]
|
186 |
+
task_history = task_history + [((file.name,), None)]
|
187 |
+
return history, task_history
|
188 |
+
|
189 |
+
def reset_user_input():
|
190 |
+
return gr.update(value="")
|
191 |
+
|
192 |
+
def reset_state(task_history):
|
193 |
+
task_history.clear()
|
194 |
+
return []
|
195 |
+
|
196 |
+
with gr.Blocks() as demo:
|
197 |
+
gr.Markdown("""<center><font size=3> Qwen2.5-VL-32B-Instruct Demo </center>""")
|
198 |
+
|
199 |
+
chatbot = gr.Chatbot(label='Qwen2.5-VL-32B-Instruct', elem_classes="control-height", height=500)
|
200 |
+
query = gr.Textbox(lines=2, label='Input')
|
201 |
+
task_history = gr.State([])
|
202 |
+
|
203 |
with gr.Row():
|
204 |
+
addfile_btn = gr.UploadButton("📁 Upload (上传文件)", file_types=["image", "video"])
|
205 |
+
submit_btn = gr.Button("🚀 Submit (发送)")
|
206 |
+
regen_btn = gr.Button("🤔️ Regenerate (重试)")
|
207 |
+
empty_bin = gr.Button("🧹 Clear History (清除历史)")
|
208 |
+
|
209 |
+
submit_btn.click(add_text, [chatbot, task_history, query], [chatbot, task_history]).then(
|
210 |
+
predict, [chatbot, task_history], [chatbot], show_progress=True
|
211 |
+
)
|
212 |
+
submit_btn.click(reset_user_input, [], [query])
|
213 |
+
empty_bin.click(reset_state, [task_history], [chatbot], show_progress=True)
|
214 |
+
regen_btn.click(regenerate, [chatbot, task_history], [chatbot], show_progress=True)
|
215 |
+
addfile_btn.upload(add_file, [chatbot, task_history, addfile_btn], [chatbot, task_history], show_progress=True)
|
216 |
+
|
217 |
+
|
218 |
+
demo.queue(default_concurrency_limit=40).launch(
|
219 |
+
share=args.share,
|
220 |
+
# inbrowser=args.inbrowser,
|
221 |
+
# server_port=args.server_port,
|
222 |
+
# server_name=args.server_name,
|
223 |
+
)
|
224 |
+
|
225 |
+
|
226 |
+
def main():
|
227 |
+
args = _get_args()
|
228 |
+
_launch_demo(args)
|
229 |
+
|
230 |
+
|
231 |
+
if __name__ == '__main__':
|
232 |
+
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|