Abhiroopvanaone commited on
Commit
eabf997
Β·
verified Β·
1 Parent(s): 6d7fe95

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +485 -4
app.py CHANGED
@@ -1,7 +1,488 @@
 
 
 
 
 
 
1
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- def greet(name):
4
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- demo = gr.Interface(fn=greet, inputs="text", outputs="text")
7
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Hugging Face Space App for GLM-4.5V CAD Generation
4
+ Optimized for HF Space deployment with GPU runtime
5
+ """
6
+
7
  import gradio as gr
8
+ import torch
9
+ from transformers import pipeline, AutoProcessor, AutoModelForImageTextToText
10
+ from PIL import Image
11
+ import json
12
+ import time
13
+ import traceback
14
+ import os
15
+
16
+ # Global model storage
17
+ models = {}
18
+ model_status = {}
19
+
20
+ def check_environment():
21
+ """Check the HF Space environment."""
22
+ info = {
23
+ "CUDA Available": torch.cuda.is_available(),
24
+ "CUDA Device Count": torch.cuda.device_count() if torch.cuda.is_available() else 0,
25
+ "Current Device": torch.cuda.current_device() if torch.cuda.is_available() else "CPU",
26
+ "GPU Name": torch.cuda.get_device_name() if torch.cuda.is_available() else "None",
27
+ "Python Version": os.sys.version,
28
+ "PyTorch Version": torch.__version__,
29
+ "Space ID": os.environ.get("SPACE_ID", "Unknown"),
30
+ "Space Author": os.environ.get("SPACE_AUTHOR_NAME", "Unknown")
31
+ }
32
+ return info
33
+
34
+ def load_model(model_name: str):
35
+ """Load GLM model with error handling."""
36
+ if model_name in models:
37
+ return True, f"βœ… {model_name} already loaded"
38
+
39
+ try:
40
+ print(f"πŸ”„ Loading {model_name}...")
41
+ model_status[model_name] = "Loading..."
42
+
43
+ # Try pipeline approach first (simpler)
44
+ pipe = pipeline(
45
+ "image-text-to-text",
46
+ model=model_name,
47
+ device_map="auto",
48
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
49
+ trust_remote_code=True
50
+ )
51
+
52
+ models[model_name] = {"pipe": pipe, "type": "pipeline"}
53
+ model_status[model_name] = "βœ… Loaded (Pipeline)"
54
+ print(f"βœ… Successfully loaded {model_name} via pipeline")
55
+ return True, f"βœ… {model_name} loaded successfully"
56
+
57
+ except Exception as e:
58
+ print(f"❌ Pipeline failed for {model_name}: {e}")
59
+
60
+ try:
61
+ # Fallback to direct loading
62
+ print(f"πŸ”„ Trying direct loading for {model_name}...")
63
+ processor = AutoProcessor.from_pretrained(model_name, trust_remote_code=True)
64
+ model = AutoModelForImageTextToText.from_pretrained(
65
+ model_name,
66
+ device_map="auto",
67
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
68
+ trust_remote_code=True
69
+ )
70
+
71
+ models[model_name] = {
72
+ "processor": processor,
73
+ "model": model,
74
+ "type": "direct"
75
+ }
76
+ model_status[model_name] = "βœ… Loaded (Direct)"
77
+ print(f"βœ… Successfully loaded {model_name} via direct loading")
78
+ return True, f"βœ… {model_name} loaded successfully (direct)"
79
+
80
+ except Exception as e2:
81
+ error_msg = f"❌ Failed to load {model_name}: {str(e2)[:200]}"
82
+ model_status[model_name] = error_msg
83
+ print(error_msg)
84
+ return False, error_msg
85
+
86
+ def generate_cadquery_code(image, model_choice, prompt_style, progress=gr.Progress()):
87
+ """Generate CADQuery code from image using selected model."""
88
+
89
+ if image is None:
90
+ return "❌ Please upload an image first."
91
+
92
+ # Model mapping
93
+ model_map = {
94
+ "GLM-4.5V": "zai-org/GLM-4.5V",
95
+ "GLM-4.5V-FP8": "zai-org/GLM-4.5V-FP8",
96
+ "GLM-4.5V-AWQ": "QuantTrio/GLM-4.5V-AWQ"
97
+ }
98
+
99
+ model_name = model_map[model_choice]
100
+
101
+ progress(0.1, desc="Loading model...")
102
+
103
+ # Load model if needed
104
+ success, message = load_model(model_name)
105
+ if not success:
106
+ return f"❌ {message}"
107
+
108
+ progress(0.3, desc="Preparing prompt...")
109
+
110
+ # Create prompt based on style
111
+ prompts = {
112
+ "Simple": "Generate CADQuery Python code for this 3D model:",
113
+
114
+ "Detailed": """Analyze this 3D CAD model and generate Python CADQuery code to recreate it.
115
+
116
+ Requirements:
117
+ - Import cadquery as cq
118
+ - Store result in 'result' variable
119
+ - Use proper CADQuery syntax
120
+ - Create complete runnable code
121
+
122
+ Code:""",
123
+
124
+ "Chain-of-Thought": """Analyze this 3D CAD model step by step:
125
+
126
+ Step 1: Identify the basic geometry (box, cylinder, etc.)
127
+ Step 2: Note any features (holes, fillets, chamfers, etc.)
128
+ Step 3: Estimate dimensions and proportions
129
+ Step 4: Generate clean CADQuery Python code
130
+
131
+ Requirements:
132
+ - Import cadquery as cq
133
+ - Store final result in 'result' variable
134
+ - Use realistic dimensions
135
+ - Include proper method chaining
136
+
137
+ ```python
138
+ import cadquery as cq
139
+
140
+ # Generated CADQuery code:"""
141
+ }
142
+
143
+ prompt = prompts[prompt_style]
144
+
145
+ progress(0.5, desc="Generating code...")
146
+
147
+ try:
148
+ start_time = time.time()
149
+
150
+ model_data = models[model_name]
151
+
152
+ if model_data["type"] == "pipeline":
153
+ # Use pipeline
154
+ messages = [
155
+ {
156
+ "role": "user",
157
+ "content": [
158
+ {"type": "image", "image": image},
159
+ {"type": "text", "text": prompt}
160
+ ]
161
+ }
162
+ ]
163
+
164
+ result = model_data["pipe"](messages, max_new_tokens=512, temperature=0.7)
165
+ generated_text = result[0]["generated_text"] if isinstance(result, list) else str(result)
166
+
167
+ else:
168
+ # Use direct model
169
+ messages = [
170
+ {
171
+ "role": "user",
172
+ "content": [
173
+ {"type": "image", "image": image},
174
+ {"type": "text", "text": prompt}
175
+ ]
176
+ }
177
+ ]
178
+
179
+ inputs = model_data["processor"].apply_chat_template(
180
+ messages,
181
+ add_generation_prompt=True,
182
+ tokenize=True,
183
+ return_dict=True,
184
+ return_tensors="pt",
185
+ )
186
+
187
+ if torch.cuda.is_available():
188
+ inputs = {k: v.to(model_data["model"].device) for k, v in inputs.items()}
189
+
190
+ outputs = model_data["model"].generate(
191
+ **inputs,
192
+ max_new_tokens=512,
193
+ temperature=0.7,
194
+ do_sample=True,
195
+ top_p=0.9
196
+ )
197
+
198
+ generated_text = model_data["processor"].tokenizer.decode(
199
+ outputs[0][inputs["input_ids"].shape[-1]:],
200
+ skip_special_tokens=True
201
+ )
202
+
203
+ generation_time = time.time() - start_time
204
+
205
+ progress(0.8, desc="Processing output...")
206
+
207
+ # Extract clean code
208
+ clean_code = extract_cadquery_code(generated_text)
209
+
210
+ progress(1.0, desc="Complete!")
211
+
212
+ # Format output
213
+ output = f"""## 🎯 Generated CADQuery Code
214
+
215
+ ```python
216
+ {clean_code}
217
+ ```
218
+
219
+ ## πŸ“Š Generation Info
220
+ - **Model**: {model_choice}
221
+ - **Time**: {generation_time:.2f} seconds
222
+ - **Prompt Style**: {prompt_style}
223
+ - **Device**: {"GPU" if torch.cuda.is_available() else "CPU"}
224
+
225
+ ## πŸ”§ Usage Instructions
226
+ 1. Copy the code above
227
+ 2. Install CADQuery: `pip install cadquery`
228
+ 3. Run the code to generate your 3D model
229
+ 4. Export to STL/STEP format if needed
230
+
231
+ ## ⚠️ Note
232
+ The generated code is AI-generated and may require manual adjustments for complex geometries.
233
+ """
234
+
235
+ return output
236
+
237
+ except Exception as e:
238
+ error_trace = traceback.format_exc()
239
+ return f"""❌ **Generation Failed**
240
+
241
+ **Error**: {str(e)}
242
+
243
+ **Model**: {model_choice}
244
+
245
+ **Traceback**:
246
+ ```
247
+ {error_trace}
248
+ ```
249
+
250
+ Try a different model variant or simpler image."""
251
+
252
+ def extract_cadquery_code(generated_text: str) -> str:
253
+ """Extract clean CADQuery code from generated text."""
254
+ text = generated_text.strip()
255
+
256
+ # Look for code blocks
257
+ if "```python" in text:
258
+ start = text.find("```python") + 9
259
+ end = text.find("```", start)
260
+ if end > start:
261
+ code = text[start:end].strip()
262
+ else:
263
+ code = text[start:].strip()
264
+ elif "```" in text:
265
+ start = text.find("```") + 3
266
+ end = text.find("```", start)
267
+ if end > start:
268
+ code = text[start:end].strip()
269
+ else:
270
+ code = text[start:].strip()
271
+ elif "import cadquery" in text.lower():
272
+ # Find the import statement and take everything after
273
+ lines = text.split('\n')
274
+ code_lines = []
275
+ started = False
276
+
277
+ for line in lines:
278
+ if "import cadquery" in line.lower() or "import cq" in line.lower():
279
+ started = True
280
+ if started:
281
+ code_lines.append(line)
282
+
283
+ code = '\n'.join(code_lines)
284
+ else:
285
+ code = text
286
+
287
+ # Basic cleanup
288
+ lines = code.split('\n')
289
+ cleaned_lines = []
290
+
291
+ for line in lines:
292
+ line = line.strip()
293
+ if line and not line.startswith('```') and not line.startswith('#') or line.startswith('# '):
294
+ cleaned_lines.append(line)
295
+
296
+ final_code = '\n'.join(cleaned_lines)
297
+
298
+ # Ensure basic structure
299
+ if "import cadquery" not in final_code and "import cq" not in final_code:
300
+ final_code = "import cadquery as cq\n\n" + final_code
301
+
302
+ # Ensure result variable
303
+ if "result" not in final_code and "=" in final_code:
304
+ lines = final_code.split('\n')
305
+ for i, line in enumerate(lines):
306
+ if "=" in line and ("cq." in line or "Workplane" in line):
307
+ lines[i] = f"result = {line.split('=', 1)[1].strip()}"
308
+ break
309
+ final_code = '\n'.join(lines)
310
+
311
+ return final_code
312
+
313
+ def get_system_info():
314
+ """Get system information for debugging."""
315
+ info = check_environment()
316
+
317
+ info_text = "## πŸ–₯️ System Information\n\n"
318
+ for key, value in info.items():
319
+ info_text += f"- **{key}**: {value}\n"
320
+
321
+ info_text += f"\n## πŸ“Š Model Status\n\n"
322
+ if model_status:
323
+ for model, status in model_status.items():
324
+ info_text += f"- **{model}**: {status}\n"
325
+ else:
326
+ info_text += "No models loaded yet.\n"
327
+
328
+ return info_text
329
+
330
+ def test_single_model(model_choice):
331
+ """Test loading a single model."""
332
+ model_map = {
333
+ "GLM-4.5V": "zai-org/GLM-4.5V",
334
+ "GLM-4.5V-FP8": "zai-org/GLM-4.5V-FP8",
335
+ "GLM-4.5V-AWQ": "QuantTrio/GLM-4.5V-AWQ"
336
+ }
337
+
338
+ model_name = model_map[model_choice]
339
+ success, message = load_model(model_name)
340
+
341
+ return f"## Test Result for {model_choice}\n\n{message}"
342
 
343
+ # Create Gradio interface
344
+ def create_interface():
345
+ """Create the Gradio interface."""
346
+
347
+ with gr.Blocks(title="GLM-4.5V CAD Generator", theme=gr.themes.Soft()) as demo:
348
+ gr.Markdown("""
349
+ # πŸ”§ GLM-4.5V CAD Code Generator
350
+
351
+ Upload a 3D CAD model image and generate CADQuery Python code using state-of-the-art vision-language models!
352
+
353
+ **Available Models:**
354
+ - **GLM-4.5V**: Full precision (best quality, 106B parameters)
355
+ - **GLM-4.5V-FP8**: 8-bit quantized (balanced performance/memory)
356
+ - **GLM-4.5V-AWQ**: AWQ quantized (fastest inference, lowest memory)
357
+ """)
358
+
359
+ with gr.Tab("πŸš€ CAD Generation"):
360
+ with gr.Row():
361
+ with gr.Column(scale=1):
362
+ image_input = gr.Image(
363
+ type="pil",
364
+ label="Upload CAD Model Image",
365
+ height=400
366
+ )
367
+
368
+ model_choice = gr.Dropdown(
369
+ choices=["GLM-4.5V", "GLM-4.5V-FP8", "GLM-4.5V-AWQ"],
370
+ value="GLM-4.5V-FP8", # Default to balanced option
371
+ label="Select GLM Model Variant"
372
+ )
373
+
374
+ prompt_style = gr.Dropdown(
375
+ choices=["Simple", "Detailed", "Chain-of-Thought"],
376
+ value="Chain-of-Thought",
377
+ label="Prompt Strategy"
378
+ )
379
+
380
+ generate_btn = gr.Button("πŸš€ Generate CADQuery Code", variant="primary", size="lg")
381
+
382
+ with gr.Column(scale=2):
383
+ output_text = gr.Markdown(
384
+ label="Generated Code",
385
+ value="Upload an image and click 'Generate' to start!"
386
+ )
387
+
388
+ generate_btn.click(
389
+ fn=generate_cadquery_code,
390
+ inputs=[image_input, model_choice, prompt_style],
391
+ outputs=output_text
392
+ )
393
+
394
+ with gr.Tab("πŸ” Model Testing"):
395
+ with gr.Row():
396
+ with gr.Column():
397
+ test_model_choice = gr.Dropdown(
398
+ choices=["GLM-4.5V", "GLM-4.5V-FP8", "GLM-4.5V-AWQ"],
399
+ value="GLM-4.5V-FP8",
400
+ label="Model to Test"
401
+ )
402
+ test_btn = gr.Button("πŸ§ͺ Test Model Loading", variant="secondary")
403
+
404
+ with gr.Column():
405
+ test_output = gr.Markdown(value="Click 'Test Model Loading' to check if models work.")
406
+
407
+ test_btn.click(
408
+ fn=test_single_model,
409
+ inputs=test_model_choice,
410
+ outputs=test_output
411
+ )
412
+
413
+ with gr.Tab("βš™οΈ System Info"):
414
+ info_output = gr.Markdown()
415
+ refresh_btn = gr.Button("πŸ”„ Refresh System Info")
416
+
417
+ # Load initial system info
418
+ demo.load(fn=get_system_info, outputs=info_output)
419
+ refresh_btn.click(fn=get_system_info, outputs=info_output)
420
+
421
+ with gr.Tab("πŸ“– Help & Examples"):
422
+ gr.Markdown("""
423
+ ## 🎯 How to Use
424
+
425
+ 1. **Upload Image**: Use clear, well-lit 3D CAD model images (PNG, JPG, JPEG)
426
+ 2. **Select Model**:
427
+ - GLM-4.5V: Best quality, slower (if you have good GPU)
428
+ - GLM-4.5V-FP8: Balanced option (recommended)
429
+ - GLM-4.5V-AWQ: Fastest, uses least memory
430
+ 3. **Choose Prompt Style**:
431
+ - Simple: Basic prompt
432
+ - Detailed: More specific requirements
433
+ - Chain-of-Thought: Step-by-step analysis (best results)
434
+ 4. **Generate**: Click the button and wait for results
435
+
436
+ ## πŸ’‘ Tips for Best Results
437
+
438
+ - Use clear, uncluttered CAD images
439
+ - Simple geometric shapes work better than complex assemblies
440
+ - Good lighting and contrast help model recognition
441
+ - Try different prompt styles if first attempt isn't satisfactory
442
+
443
+ ## πŸ“ Example Use Cases
444
+
445
+ - Mechanical parts (brackets, housings, gears)
446
+ - Architectural elements (columns, beams, panels)
447
+ - Product design components
448
+ - Educational CAD modeling
449
+
450
+ ## βš™οΈ Generated Code Usage
451
+
452
+ ```python
453
+ # Install CADQuery
454
+ pip install cadquery
455
+
456
+ # Run generated code
457
+ import cadquery as cq
458
+ # ... your generated code ...
459
+
460
+ # Export result
461
+ cq.exporters.export(result, "model.stl")
462
+ ```
463
+
464
+ ## πŸ› Troubleshooting
465
+
466
+ - **Model loading fails**: Try a different variant (FP8 or AWQ)
467
+ - **Generation is slow**: Use GLM-4.5V-AWQ for faster results
468
+ - **Code has errors**: Try Chain-of-Thought prompt style
469
+ - **Poor results**: Ensure image is clear and well-lit
470
+ """)
471
+
472
+ return demo
473
 
474
+ if __name__ == "__main__":
475
+ print("πŸš€ Starting GLM-4.5V CAD Generator...")
476
+ print("Environment check:")
477
+ info = check_environment()
478
+ for key, value in info.items():
479
+ print(f" {key}: {value}")
480
+
481
+ # Create and launch the interface
482
+ demo = create_interface()
483
+ demo.launch(
484
+ share=False, # HF Spaces automatically provides public URL
485
+ server_name="0.0.0.0",
486
+ server_port=7860,
487
+ show_error=True
488
+ )