yejunliang23 commited on
Commit
f77d097
·
unverified ·
1 Parent(s): 34ffddc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -232
app.py CHANGED
@@ -1,237 +1,232 @@
1
- import gradio as gr
 
 
 
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
- def apply_gradient_color(mesh_text):
68
- """
69
- Apply a gradient color to the mesh vertices based on the Y-axis and save as GLB.
70
- Args:
71
- mesh_text (str): The input mesh in OBJ format as a string.
72
- Returns:
73
- str: Path to the GLB file with gradient colors applied.
74
- """
75
- # Load the mesh
76
- temp_file = tempfile.NamedTemporaryFile(suffix=f"", delete=False).name#"temp_mesh.obj"
77
- with open(temp_file+".obj", "w") as f:
78
- f.write(mesh_text)
79
- # return temp_file
80
- mesh = trimesh.load_mesh(temp_file+".obj", file_type='obj')
81
-
82
- # Get vertex coordinates
83
- vertices = mesh.vertices
84
- y_values = vertices[:, 1] # Y-axis values
85
-
86
- # Normalize Y values to range [0, 1] for color mapping
87
- y_normalized = (y_values - y_values.min()) / (y_values.max() - y_values.min())
88
-
89
- # Generate colors: Map normalized Y values to RGB gradient (e.g., blue to red)
90
- colors = np.zeros((len(vertices), 4)) # RGBA
91
- colors[:, 0] = y_normalized # Red channel
92
- colors[:, 2] = 1 - y_normalized # Blue channel
93
- colors[:, 3] = 1.0 # Alpha channel (fully opaque)
94
-
95
- # Attach colors to mesh vertices
96
- mesh.visual.vertex_colors = colors
97
-
98
- # Export to GLB format
99
- glb_path = temp_file+".glb"
100
- with open(glb_path, "wb") as f:
101
- f.write(export_glb(mesh))
102
-
103
- return glb_path
104
-
105
- def visualize_mesh(mesh_text):
106
- """
107
- Convert the provided 3D mesh text into a visualizable format.
108
- This function assumes the input is in OBJ format.
109
- """
110
- temp_file = "temp_mesh.obj"
111
- with open(temp_file, "w") as f:
112
- f.write(mesh_text)
113
- return temp_file
114
-
115
- @spaces.GPU(duration=120)
116
- def chat_llama3_8b(message: str,
117
- history: list,
118
- temperature: float,
119
- max_new_tokens: int
120
- ) -> str:
121
- """
122
- Generate a streaming response using the llama3-8b model.
123
- Args:
124
- message (str): The input message.
125
- history (list): The conversation history used by ChatInterface.
126
- temperature (float): The temperature for generating the response.
127
- max_new_tokens (int): The maximum number of new tokens to generate.
128
- Returns:
129
- str: The generated response.
130
- """
131
- conversation = []
132
- for user, assistant in history:
133
- conversation.extend([{"role": "user", "content": user}, {"role": "assistant", "content": assistant}])
134
- conversation.append({"role": "user", "content": message})
135
-
136
- input_ids = tokenizer.apply_chat_template(conversation, return_tensors="pt").to(model.device)
137
-
138
- streamer = TextIteratorStreamer(tokenizer, timeout=10.0, skip_prompt=True, skip_special_tokens=True)
139
- # print(max_new_tokens)
140
- max_new_tokens=4096
141
- temperature=0.9
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
- # This will enforce greedy generation (do_sample=False) when the temperature is passed 0, avoiding the crash.
151
- if temperature == 0:
152
- generate_kwargs['do_sample'] = False
153
-
154
- t = Thread(target=model.generate, kwargs=generate_kwargs)
155
- t.start()
156
-
157
- outputs = []
158
- for text in streamer:
159
- outputs.append(text)
160
- #print(outputs)
161
- yield "".join(outputs)
162
-
163
-
164
- # Gradio block
165
- chatbot=gr.Chatbot(height=450, placeholder=PLACEHOLDER, label='Gradio ChatInterface')
166
-
167
- with gr.Blocks(fill_height=True, css=css) as demo:
168
- with gr.Column():
169
- gr.Markdown(DESCRIPTION)
170
- # gr.DuplicateButton(value="Duplicate Space for private use", elem_id="duplicate-button")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  with gr.Row():
172
- with gr.Column(scale=3):
173
- gr.ChatInterface(
174
- fn=chat_llama3_8b,
175
- chatbot=chatbot,
176
- fill_height=True,
177
- additional_inputs_accordion=gr.Accordion(label="⚙️ Parameters", open=False, render=False),
178
- additional_inputs=[
179
- gr.Slider(minimum=0,
180
- maximum=1,
181
- step=0.1,
182
- value=0.9,
183
- label="Temperature",
184
- interactive = False,
185
- render=False),
186
- gr.Slider(minimum=128,
187
- maximum=4096,
188
- step=1,
189
- value=4096,
190
- label="Max new tokens",
191
- interactive = False,
192
- render=False),
193
- ],
194
- examples=[
195
- ['Create a 3D model of a wooden hammer'],
196
- ['Create a 3D model of a pyramid in obj format'],
197
- ['Create a 3D model of a cabinet.'],
198
- ['Create a low poly 3D model of a coffe cup'],
199
- ['Create a 3D model of a table.'],
200
- ["Create a low poly 3D model of a tree."],
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("<", "&lt;")
67
+ line = line.replace(">", "&gt;")
68
+ line = line.replace(" ", "&nbsp;")
69
+ line = line.replace("*", "&ast;")
70
+ line = line.replace("_", "&lowbar;")
71
+ line = line.replace("-", "&#45;")
72
+ line = line.replace(".", "&#46;")
73
+ line = line.replace("!", "&#33;")
74
+ line = line.replace("(", "&#40;")
75
+ line = line.replace(")", "&#41;")
76
+ line = line.replace("$", "&#36;")
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()