Derr11 commited on
Commit
30dcad4
·
verified ·
1 Parent(s): d3bdf54

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +338 -536
app.py CHANGED
@@ -6,611 +6,413 @@ import spaces
6
  import warnings
7
  warnings.filterwarnings("ignore")
8
 
9
- from transformers import Qwen3OmniMoeForConditionalGeneration, Qwen3OmniMoeProcessor
10
- from qwen_omni_utils import process_mm_info
11
 
12
  # =========================================================
13
- # Patches لتجاوز مشاكل التوافق في Qwen3-Omni
14
  # =========================================================
15
 
16
- def _patched_mark_tied_weights_as_initialized(self):
17
- """
18
- تجاوز مشكلة lm_head في tied weights
19
- """
20
- return
21
-
22
- def _patched_init_weights(self, module):
23
- """
24
- تجاوز مشكلة initializer_range في Qwen3OmniMoeTalkerConfig
25
- """
26
- # نحاول الحصول على initializer_range، وإذا لم يكن موجود نستخدم قيمة افتراضية
27
- try:
28
- std = self.config.initializer_range
29
- except AttributeError:
30
- # قيمة افتراضية آمنة
31
- std = 0.02
32
-
33
- # تطبيق التهيئة الأساسية
34
- if isinstance(module, torch.nn.Linear):
35
- module.weight.data.normal_(mean=0.0, std=std)
36
- if module.bias is not None:
37
- module.bias.data.zero_()
38
- elif isinstance(module, torch.nn.Embedding):
39
- module.weight.data.normal_(mean=0.0, std=std)
40
- if module.padding_idx is not None:
41
- module.weight.data[module.padding_idx].zero_()
42
-
43
- def _patched_initialize_weights(self):
44
- """
45
- تجاوز كامل لدالة initialize_weights في حالة استمرار المشاكل
46
- """
47
- # نحاول التهيئة العادية، وإذا فشلت نتجاهلها
48
- try:
49
- # محاولة استخدام الدالة الأصلية إذا كانت موجودة
50
- if hasattr(self, '_original_initialize_weights'):
51
- self._original_initialize_weights()
52
- else:
53
- # تهيئة بسيطة آمنة
54
- for module in self.modules():
55
- if isinstance(module, (torch.nn.Linear, torch.nn.Embedding)):
56
- if hasattr(module, 'weight'):
57
- module.weight.data.normal_(mean=0.0, std=0.02)
58
- if hasattr(module, 'bias') and module.bias is not None:
59
- module.bias.data.zero_()
60
- except Exception as e:
61
- print(f"Warning: Could not initialize weights properly: {e}")
62
- # نستمر بدون تهيئة - النموذج المحمل مسبقاً يجب أن يعمل
63
-
64
- # تطبيق الـ patches قبل أي استدعاء لـ from_pretrained
65
- def apply_patches():
66
- """تطبيق جميع الـ patches اللازمة"""
67
-
68
- # Patch 1: تجاوز مشكلة lm_head
69
- if hasattr(Qwen3OmniMoeForConditionalGeneration, "mark_tied_weights_as_initialized"):
70
- Qwen3OmniMoeForConditionalGeneration.mark_tied_weights_as_initialized = (
71
- _patched_mark_tied_weights_as_initialized
72
- )
73
-
74
- # Patch 2: تجاوز مشكلة initializer_range
75
- if hasattr(Qwen3OmniMoeForConditionalGeneration, "_init_weights"):
76
- Qwen3OmniMoeForConditionalGeneration._init_weights = _patched_init_weights
77
-
78
- # Patch 3: تجاوز initialize_weights بالكامل إذا لزم
79
- if hasattr(Qwen3OmniMoeForConditionalGeneration, "initialize_weights"):
80
- # حفظ الدالة الأصلية
81
- Qwen3OmniMoeForConditionalGeneration._original_initialize_weights = (
82
- Qwen3OmniMoeForConditionalGeneration.initialize_weights
83
- )
84
- Qwen3OmniMoeForConditionalGeneration.initialize_weights = _patched_initialize_weights
85
-
86
- # تطبيق الـ patches
87
- apply_patches()
88
-
89
- # =========================================================
90
- # إعدادات عامة
91
- # =========================================================
92
 
93
- MODEL_PATH = os.getenv("MODEL_PATH", "Qwen/Qwen3-Omni-30B-A3B-Instruct")
94
- USE_AUDIO_IN_VIDEO = True # استخدام الصوت داخل الفيديو إذا وجد
 
 
 
 
 
95
 
96
- VOICE_CHOICES = ["Ethan", "Chelsie", "Aiden"]
97
- DEFAULT_VOICE = "Ethan"
98
-
99
- # سنحمّل النموذج كسولياً (عند أول استدعاء فقط)
100
  model = None
101
- processor = None
102
 
103
 
104
  def load_model():
105
  """
106
- تحميل Qwen3-Omni والمعالج عند أول استدعاء فقط.
107
- - نستخدم attn_implementation="eager" لتفادي الحاجة لـ flash-attn.
108
- - نضيف low_cpu_mem_usage=True لتحسين الأداء
109
- - نضيف ignore_mismatched_sizes=True لتجاوز مشاكل الأحجام
110
  """
111
- global model, processor
112
-
113
- if model is not None and processor is not None:
114
  return
115
-
116
- print(f"[ZeroGPU] Loading model from: {MODEL_PATH}")
117
-
118
- # اختيار نوع البيانات والجهاز
119
  if torch.cuda.is_available():
120
- torch_dtype = torch.bfloat16
121
  device = "cuda"
 
122
  else:
 
123
  torch_dtype = torch.float32
124
- device = "cpu"
125
-
126
- try:
127
- # محاولة تحميل النموذج مع خيارات إضافية للأمان
128
- local_model = Qwen3OmniMoeForConditionalGeneration.from_pretrained(
129
- MODEL_PATH,
130
- torch_dtype=torch_dtype,
131
- attn_implementation="eager", # آمن على ZeroGPU بدون flash-attn
132
- low_cpu_mem_usage=True, # تحسين استخدام الذاكرة
133
- ignore_mismatched_sizes=True, # تجاوز مشاكل الأحجام
134
- trust_remote_code=True, # السماح بالكود المخصص
135
- )
136
-
137
- # نقل النموذج إلى الجهاز المناسب
138
- local_model = local_model.to(device)
139
-
140
- # وضع النموذج في وضع التقييم (inference)
141
- local_model.eval()
142
-
143
- except Exception as e:
144
- print(f"Error loading model: {e}")
145
- print("Attempting alternative loading method...")
146
-
147
- # محاولة بديلة مع تعطيل _init_weights
148
- try:
149
- # تعطيل _init_weights مؤقتاً
150
- original_init_weights = None
151
- if hasattr(Qwen3OmniMoeForConditionalGeneration, "_init_weights"):
152
- original_init_weights = Qwen3OmniMoeForConditionalGeneration._init_weights
153
- Qwen3OmniMoeForConditionalGeneration._init_weights = lambda self, module: None
154
-
155
- local_model = Qwen3OmniMoeForConditionalGeneration.from_pretrained(
156
- MODEL_PATH,
157
- torch_dtype=torch_dtype,
158
- attn_implementation="eager",
159
- low_cpu_mem_usage=True,
160
- )
161
-
162
- # استعادة _init_weights
163
- if original_init_weights:
164
- Qwen3OmniMoeForConditionalGeneration._init_weights = original_init_weights
165
-
166
- local_model = local_model.to(device)
167
- local_model.eval()
168
-
169
- except Exception as e2:
170
- raise RuntimeError(f"Failed to load model: {e2}")
171
-
172
- # تحميل المعالج
173
- try:
174
- local_processor = Qwen3OmniMoeProcessor.from_pretrained(
175
- MODEL_PATH,
176
- trust_remote_code=True
177
- )
178
- except Exception as e:
179
- print(f"Error loading processor: {e}")
180
- raise
181
-
182
- model = local_model
183
- processor = local_processor
184
- print(f"[ZeroGPU] Model loaded successfully on {device} with dtype {torch_dtype}.")
185
-
186
-
187
- def build_messages_from_history(
188
- history,
189
- system_prompt,
190
- user_text,
191
- image,
192
- audio_path,
193
- video_path,
194
- ):
195
- """
196
- تحويل تاريخ الدردشة + المدخل الحالي إلى conversation بالـ format
197
- المطلوب من Qwen3-Omni.
198
- history: list of [user_text, assistant_text]
199
- """
200
- messages = []
201
-
202
- if system_prompt:
203
- messages.append(
204
- {
205
- "role": "system",
206
- "content": [{"type": "text", "text": system_prompt}],
207
- }
208
- )
209
-
210
- # تاريخ المحادثة
211
- for user_msg, assistant_msg in history:
212
- if user_msg:
213
- messages.append(
214
- {
215
- "role": "user",
216
- "content": [{"type": "text", "text": user_msg}],
217
- }
218
- )
219
- if assistant_msg:
220
- messages.append(
221
- {
222
- "role": "assistant",
223
- "content": [{"type": "text", "text": assistant_msg}],
224
- }
225
- )
226
-
227
- # محتوى رسالة المستخدم الحالية
228
- user_content = []
229
-
230
- if image is not None:
231
- user_content.append({"type": "image", "image": image})
232
-
233
- if audio_path is not None and audio_path != "":
234
- user_content.append({"type": "audio", "audio": audio_path})
235
-
236
- if video_path is not None and video_path != "":
237
- user_content.append({"type": "video", "video": video_path})
238
-
239
- if user_text and user_text.strip():
240
- user_content.append({"type": "text", "text": user_text.strip()})
241
 
242
- if user_content:
243
- messages.append(
244
- {
245
- "role": "user",
246
- "content": user_content,
247
- }
248
- )
249
 
250
- return messages
 
 
 
 
 
 
251
 
252
 
253
  # =========================================================
254
- # دالة الاستدلال (تعمل على ZeroGPU)
255
  # =========================================================
256
 
257
  @spaces.GPU(duration=120)
258
- def qwen3_omni_inference(
259
- history,
260
- user_text,
261
- image,
262
- audio_path,
263
- video_path,
264
- system_prompt,
265
- return_audio,
266
- speaker,
267
  temperature,
268
  top_p,
269
- max_tokens,
 
 
270
  ):
271
  """
272
- - تنفيذ الاستدلال على ZeroGPU.
273
- - يدعم نص + صورة + صوت + فيديو في نفس الرسالة.
274
- - مخرج نصي دائماً، ومخرج صوتي اختياري.
275
  """
276
-
277
- # في حالة عدم وجود مداخل من المستخدم
278
- if not (user_text or image is not None or audio_path or video_path):
279
- return history, None, "", None, None, None
280
-
281
- try:
282
- load_model()
283
- global model, processor
284
-
285
- messages = build_messages_from_history(
286
- history=history,
287
- system_prompt=system_prompt,
288
- user_text=user_text,
289
- image=image,
290
- audio_path=audio_path,
291
- video_path=video_path,
292
- )
293
-
294
- # بناء نص المحادثة باستخدام chat_template
295
- text_prompt = processor.apply_chat_template(
296
- messages,
297
- add_generation_prompt=True,
298
- tokenize=False,
299
- )
300
-
301
- # تجهيز الوسائط المتعددة
302
- audios, images, videos = process_mm_info(
303
- messages,
304
- use_audio_in_video=USE_AUDIO_IN_VIDEO,
305
- )
306
-
307
- # تحويل إلى تينسورات
308
- inputs = processor(
309
- text=text_prompt,
310
- audio=audios,
311
- images=images,
312
- videos=videos,
313
- return_tensors="pt",
314
- padding=True,
315
- use_audio_in_video=USE_AUDIO_IN_VIDEO,
316
- )
317
-
318
- # نقل إلى جهاز النموذج ونفس dtype
319
- first_param = next(model.parameters())
320
- device = first_param.device
321
 
322
- # تحويل المدخلات إلى الجهاز المناسب
323
- for key in inputs:
324
- if hasattr(inputs[key], 'to'):
325
- inputs[key] = inputs[key].to(device)
326
-
327
- # إعدادات التوليد
328
- gen_kwargs = dict(
329
- temperature=float(temperature) if temperature > 0 else 1e-7,
330
- top_p=float(top_p),
331
- max_new_tokens=int(max_tokens),
332
- do_sample=temperature > 0,
333
- use_audio_in_video=USE_AUDIO_IN_VIDEO,
334
- )
335
 
336
- # إضافة thinker_return_dict_in_generate فقط إذا كان مدعوماً
337
- if hasattr(model, 'config') and hasattr(model.config, 'thinker_return_dict_in_generate'):
338
- gen_kwargs["thinker_return_dict_in_generate"] = True
339
-
340
- # توليد نص فقط أو نص + صوت
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  with torch.no_grad():
342
- if not return_audio:
343
- gen_kwargs["return_audio"] = False
344
- outputs = model.generate(**inputs, **gen_kwargs)
345
- text_ids = outputs
346
- audio_out = None
 
 
 
 
 
 
 
 
347
  else:
348
- gen_kwargs["speaker"] = speaker
349
- gen_kwargs["return_audio"] = True
350
- outputs = model.generate(**inputs, **gen_kwargs)
351
- if isinstance(outputs, tuple):
352
- text_ids, audio_out = outputs
353
- else:
354
- text_ids = outputs
355
- audio_out = None
356
-
357
- # استخراج النص الناتج (بدون مدخل prompt)
358
- input_len = inputs["input_ids"].shape[1]
359
-
360
- # التعامل مع الأنواع المختلفة من المخرجات
361
- if hasattr(text_ids, 'sequences'):
362
- generated_ids = text_ids.sequences[:, input_len:]
363
- else:
364
- generated_ids = text_ids[:, input_len:]
365
 
366
- generated_text = processor.batch_decode(
367
- generated_ids,
368
- skip_special_tokens=True,
369
- clean_up_tokenization_spaces=False,
370
- )[0]
371
-
372
- # تحديث تاريخ الدردشة
373
- user_display = (
374
- user_text if (user_text and user_text.strip()) else "[Multimodal message]"
375
- )
376
- history = history + [[user_display, generated_text]]
377
-
378
- # تجهيز الصوت الناتج إن وجد
379
- gr_audio = None
380
- if audio_out is not None:
381
  try:
382
- audio_np = audio_out.reshape(-1).detach().cpu().numpy()
383
- sample_rate = 24000
384
- gr_audio = (sample_rate, audio_np.astype(np.float32))
 
 
385
  except Exception as e:
386
- print(f"Warning: Could not process audio output: {e}")
387
- gr_audio = None
388
-
389
- # نعيد: history الجديد + صوت الرد + تفريغ مدخلات المستخدم
390
- return history, gr_audio, "", None, None, None
391
 
392
  except Exception as e:
393
- print(f"Error during inference: {e}")
394
  import traceback
395
  traceback.print_exc()
396
-
397
- # في حالة الخطأ، نضيف رسالة خطأ للمحادثة
398
- user_display = (
399
- user_text if (user_text and user_text.strip()) else "[Multimodal message]"
400
- )
401
- error_message = f"عذراً، حدث خطأ أثناء معالجة الرسالة: {str(e)}"
402
- history = history + [[user_display, error_message]]
403
- return history, None, "", None, None, None
404
 
405
 
406
  # =========================================================
407
- # دوال واجهة Gradio
408
  # =========================================================
409
 
410
- def clear_chat():
411
- """إعادة تعيين المحادثة ومخرج الصوت."""
412
- return [], None
413
-
414
-
415
  def create_interface():
416
- """إنشاء واجهة Gradio للدردشة"""
417
- with gr.Blocks(title="Qwen3-Omni-30B-A3B – ZeroGPU Chat") as demo:
 
418
  gr.Markdown(
419
  """
420
- <h1 style="text-align:center;">🤖 Qwen3-Omni-30B-A3B ZeroGPU Chat</h1>
421
- <p style="text-align:center;">
422
- دردشة متعددة الوسائط (نص + صورة + صوت + فيديو) تعمل على ZeroGPU.<br/>
423
- اكتب رسالتك، ويمكنك إضافة صورة/صوت/فيديو، ثم اضغط <b>إرسال</b> أو Enter.<br/>
424
- (لإضافة سطر جديد استخدم Shift+Enter)
425
- </p>
 
 
 
 
 
426
  """
427
  )
428
-
429
  with gr.Row():
430
- # العمود الأيسر: المحادثة
431
  with gr.Column(scale=3):
432
- chatbot = gr.Chatbot(
433
- label="المحادثة",
434
- height=480,
435
- elem_id="chatbot",
 
 
436
  )
437
-
438
- audio_output = gr.Audio(
439
- label="رد النموذج (صوت)",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  type="numpy",
441
- autoplay=True,
442
- visible=True,
443
  )
444
-
445
- with gr.Row():
446
- user_text = gr.Textbox(
447
- label="رسالتك",
448
- placeholder="اكتب رسالتك هنا (يمكنك أيضاً إرفاق صورة/صوت/فيديو من الأسفل)...",
449
- lines=3,
450
- show_label=False,
451
- elem_id="message",
452
- )
453
-
454
- with gr.Row():
455
- image_input = gr.Image(
456
- label="📷 صورة (اختياري)",
457
- type="pil",
458
- sources=["upload", "webcam"],
459
- height=150,
460
- )
461
- audio_input = gr.Audio(
462
- label="🎙️ صوت (اختياري)",
463
- type="filepath",
464
- sources=["microphone", "upload"],
465
- )
466
- video_input = gr.Video(
467
- label="🎬 فيديو (اختياري)",
468
- height=150,
469
- )
470
-
471
- with gr.Row():
472
- send_btn = gr.Button("إرسال", variant="primary", scale=2)
473
- clear_btn = gr.Button("مسح المحادثة", variant="secondary")
474
-
475
- # العمود الأيمن: الإعدادات
476
  with gr.Column(scale=1):
477
- gr.Markdown("### ⚙️ إعدادات النموذج")
478
-
479
- system_prompt = gr.Textbox(
480
- label="System Prompt",
481
- value="You are a helpful, multilingual assistant.",
482
- lines=4,
483
- placeholder="يمكنك التحكم في شخصية النموذج من هنا (اختياري).",
 
484
  )
485
-
486
- return_audio = gr.Checkbox(
487
- label="تفعيل مخرج صوتي (النموذج يتكلم)؟",
488
- value=True,
 
 
 
489
  )
490
-
491
- speaker = gr.Dropdown(
492
- label="صوت المتحدث (speaker)",
493
- choices=VOICE_CHOICES,
494
- value=DEFAULT_VOICE,
 
 
495
  )
496
-
497
- with gr.Accordion("إعدادات متقدمة", open=False):
498
- temperature = gr.Slider(
499
- label="Temperature (العشوائية)",
500
- minimum=0.0,
501
- maximum=1.5,
502
- value=0.6,
503
- step=0.05,
504
- )
505
-
506
- top_p = gr.Slider(
507
- label="Top-p (حجم العينة)",
508
- minimum=0.1,
509
- maximum=1.0,
510
- value=0.95,
511
- step=0.05,
512
- )
513
-
514
- max_tokens = gr.Slider(
515
- label="Max new tokens (طول الرد الأقصى)",
516
- minimum=16,
517
- maximum=1024,
518
- value=384,
519
- step=16,
520
- )
521
-
522
- gr.Markdown(
523
- """
524
- **📝 ملاحظات:**
525
- - يمكنك إرسال نص فقط، أو نص مع صورة/صوت/فيديو في رسالة واحدة
526
- - Enter للإرسال، Shift+Enter لسطر جديد
527
- - تشغيل النموذج على ZeroGPU قد يستغرق عدة ثوانٍ حسب طول الرسالة
528
- - النموذج يدعم اللغات المتعددة بما فيها العربية والإنجليزية
529
- """
530
  )
531
-
532
- # حالة المحادثة
533
- history_state = gr.State([])
534
-
535
- # مدخلات دالة الإرسال
536
- send_inputs = [
537
- history_state,
538
- user_text,
539
- image_input,
540
- audio_input,
541
- video_input,
542
- system_prompt,
543
- return_audio,
544
- speaker,
545
- temperature,
546
- top_p,
547
- max_tokens,
548
- ]
549
-
550
- send_outputs = [
551
- history_state,
552
- audio_output,
553
- user_text,
554
- image_input,
555
- audio_input,
556
- video_input,
557
- ]
558
-
559
- # إرسال بالزر
560
- send_btn.click(
561
- fn=qwen3_omni_inference,
562
- inputs=send_inputs,
563
- outputs=send_outputs,
564
- queue=True,
565
- ).then(
566
- lambda h: h,
567
- inputs=history_state,
568
- outputs=chatbot,
569
  )
570
-
571
- # إرسال بالـ Enter من Textbox
572
- user_text.submit(
573
- fn=qwen3_omni_inference,
574
- inputs=send_inputs,
575
- outputs=send_outputs,
576
- queue=True,
577
- ).then(
578
- lambda h: h,
579
- inputs=history_state,
580
- outputs=chatbot,
581
  )
582
-
583
- # مسح المحادثة
584
- clear_btn.click(
585
- fn=clear_chat,
586
- inputs=None,
587
- outputs=[history_state, audio_output],
588
- ).then(
589
- lambda: [],
590
- inputs=None,
591
- outputs=chatbot,
592
- ).then(
593
- lambda: ("", None, None, None),
594
- inputs=None,
595
- outputs=[user_text, image_input, audio_input, video_input],
 
 
 
596
  )
597
-
598
- # رسالة تحميل النموذج عند بدء التطبيق
599
- demo.load(
600
- lambda: gr.Info("جاري تحميل النموذج... قد يستغرق هذا بضع دقائق في المرة الأولى."),
601
- inputs=None,
602
- outputs=None,
 
 
 
 
603
  )
604
-
605
  return demo
606
 
607
 
608
- # إنشاء الواجهة
609
- demo = create_interface()
 
610
 
611
  if __name__ == "__main__":
612
- # إطلاق التطبيق
613
  demo.launch(
614
- ssr_mode=False, # تعطيل SSR لتجنب مشاكل "Starting..."
615
- show_error=True, # عرض الأخطاء بشكل واضح
 
616
  )
 
6
  import warnings
7
  warnings.filterwarnings("ignore")
8
 
9
+ from PIL import Image
10
+ from transformers import AutoModel, AutoTokenizer
11
 
12
  # =========================================================
13
+ # إعدادات النموذج
14
  # =========================================================
15
 
16
+ MODEL_PATH = "openbmb/MiniCPM-o-2_6"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ # النموذج يدعم:
19
+ # - Vision (الصور)
20
+ # - Audio (الصوت)
21
+ # - TTS (تحويل النص إلى كلام)
22
+ # - ASR (التعرف على الكلام)
23
+ # - Video (الفيديو)
24
+ # - Voice Cloning (استنساخ الصوت)
25
 
 
 
 
 
26
  model = None
27
+ tokenizer = None
28
 
29
 
30
  def load_model():
31
  """
32
+ تحميل MiniCPM-o-2_6 مع دعم جميع الوسائط
 
 
 
33
  """
34
+ global model, tokenizer
35
+
36
+ if model is not None and tokenizer is not None:
37
  return
38
+
39
+ print(f"[ZeroGPU] Loading MiniCPM-o-2_6...")
40
+
41
+ # اختيار الجهاز ونوع البيانات
42
  if torch.cuda.is_available():
 
43
  device = "cuda"
44
+ torch_dtype = torch.bfloat16
45
  else:
46
+ device = "cpu"
47
  torch_dtype = torch.float32
48
+
49
+ # تحميل النموذج مع جميع القدرات
50
+ model = AutoModel.from_pretrained(
51
+ MODEL_PATH,
52
+ trust_remote_code=True,
53
+ attn_implementation='sdpa', # sdpa أو flash_attention_2
54
+ torch_dtype=torch_dtype,
55
+ init_vision=True, # تفعيل الرؤية
56
+ init_audio=True, # تفعيل الصوت
57
+ init_tts=True # تفعيل TTS
58
+ )
59
+
60
+ model = model.eval().to(device)
61
+
62
+ # تحميل tokenizer
63
+ tokenizer = AutoTokenizer.from_pretrained(
64
+ MODEL_PATH,
65
+ trust_remote_code=True
66
+ )
67
+
68
+ print(f"[ZeroGPU] Model loaded successfully on {device}")
69
+ ```<!--citation:1-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
+ ```python
72
+ # =========================================================
73
+ # دالة معالجة الصور
74
+ # =========================================================
 
 
 
75
 
76
+ def process_image(image_path_or_pil):
77
+ """معالجة الصورة للنموذج"""
78
+ if isinstance(image_path_or_pil, str):
79
+ image = Image.open(image_path_or_pil).convert('RGB')
80
+ else:
81
+ image = image_path_or_pil.convert('RGB')
82
+ return image
83
 
84
 
85
  # =========================================================
86
+ # دالة الاستدلال الرئيسية (مع دعم ZeroGPU)
87
  # =========================================================
88
 
89
  @spaces.GPU(duration=120)
90
+ def minicpm_o_inference(
91
+ text_input,
92
+ image_input,
93
+ audio_input,
94
+ video_input,
95
+ mode,
 
 
 
96
  temperature,
97
  top_p,
98
+ max_new_tokens,
99
+ enable_tts,
100
+ tts_style
101
  ):
102
  """
103
+ دالة الاستدلال الرئيسية لـ MiniCPM-o-2_6
104
+ تدعم: نص، صورة، صوت، فيديو
 
105
  """
106
+
107
+ load_model()
108
+ global model, tokenizer
109
+
110
+ # بناء الرسائل حسب نوع المدخل
111
+ messages = []
112
+
113
+ # إضافة المحتوى حسب نوع المدخل
114
+ if mode == "Text Only":
115
+ if not text_input:
116
+ return "Please provide text input.", None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ messages = [
119
+ {"role": "user", "content": text_input}
120
+ ]
 
 
 
 
 
 
 
 
 
 
121
 
122
+ elif mode == "Image + Text":
123
+ if not image_input:
124
+ return "Please provide an image.", None
125
+
126
+ image = process_image(image_input)
127
+
128
+ # صياغة السؤال
129
+ question = text_input if text_input else "What is shown in this image?"
130
+
131
+ messages = [
132
+ {
133
+ "role": "user",
134
+ "content": [
135
+ Image.open(image_input) if isinstance(image_input, str) else image_input,
136
+ question
137
+ ]
138
+ }
139
+ ]
140
+
141
+ elif mode == "Audio + Text":
142
+ if not audio_input:
143
+ return "Please provide audio input.", None
144
+
145
+ # معالجة الصوت
146
+ question = text_input if text_input else "What is the content of this audio?"
147
+
148
+ # النموذج يدعم الصوت مباشرة
149
+ messages = [
150
+ {
151
+ "role": "user",
152
+ "content": [
153
+ {"type": "audio", "audio": audio_input},
154
+ {"type": "text", "text": question}
155
+ ]
156
+ }
157
+ ]
158
+
159
+ elif mode == "Video + Text":
160
+ if not video_input:
161
+ return "Please provide a video.", None
162
+
163
+ question = text_input if text_input else "What happens in this video?"
164
+
165
+ # معالجة الفيديو
166
+ messages = [
167
+ {
168
+ "role": "user",
169
+ "content": [
170
+ {"type": "video", "video": video_input},
171
+ {"type": "text", "text": question}
172
+ ]
173
+ }
174
+ ]
175
+
176
+ # إعدادات التوليد
177
+ generation_config = {
178
+ "max_new_tokens": max_new_tokens,
179
+ "temperature": temperature,
180
+ "top_p": top_p,
181
+ "do_sample": temperature > 0,
182
+ }
183
+
184
+ try:
185
+ # التوليد
186
  with torch.no_grad():
187
+ if mode == "Image + Text" and image_input:
188
+ # معالجة خاصة للصور
189
+ image = process_image(image_input)
190
+ question = text_input if text_input else "What is shown in this image?"
191
+
192
+ # استخدام chat للصور
193
+ response = model.chat(
194
+ image=image,
195
+ msgs=[{"role": "user", "content": question}],
196
+ tokenizer=tokenizer,
197
+ **generation_config
198
+ )
199
+
200
  else:
201
+ # للنص والأنواع الأخرى
202
+ inputs = tokenizer(messages, return_tensors="pt")
203
+ inputs = inputs.to(model.device)
204
+
205
+ outputs = model.generate(
206
+ **inputs,
207
+ **generation_config
208
+ )
209
+
210
+ response = tokenizer.decode(
211
+ outputs[0][inputs['input_ids'].shape[1]:],
212
+ skip_special_tokens=True
213
+ )
 
 
 
 
214
 
215
+ # إذا كان TTS مفعل، نولد صوت
216
+ audio_output = None
217
+ if enable_tts and isinstance(response, str):
 
 
 
 
 
 
 
 
 
 
 
 
218
  try:
219
+ # استخدام TTS المدمج في النموذج
220
+ audio_output = model.generate_speech(
221
+ text=response,
222
+ style=tts_style
223
+ )
224
  except Exception as e:
225
+ print(f"TTS generation failed: {e}")
226
+ audio_output = None
227
+
228
+ return response, audio_output
 
229
 
230
  except Exception as e:
 
231
  import traceback
232
  traceback.print_exc()
233
+ return f"Error: {str(e)}", None
 
 
 
 
 
 
 
234
 
235
 
236
  # =========================================================
237
+ # واجهة Gradio
238
  # =========================================================
239
 
 
 
 
 
 
240
  def create_interface():
241
+ """إنشاء واجهة Gradio لـ MiniCPM-o-2_6"""
242
+
243
+ with gr.Blocks(title="MiniCPM-o-2_6 - Multimodal AI") as demo:
244
  gr.Markdown(
245
  """
246
+ # 🤖 MiniCPM-o-2_6 - Multimodal AI Assistant
247
+
248
+ **القدرات:**
249
+ - 🖼️ فهم الصور (OCR، وصف، تحليل)
250
+ - 🎙️ معالجة الصوت (ASR، فهم المحتوى)
251
+ - 🎬 تحليل الفيديو
252
+ - 🗣️ تحويل النص إلى كلام (TTS)
253
+ - 🎭 استنساخ الصوت
254
+ - 💬 محادثة في الوقت الفعلي
255
+
256
+ **الأداء:** يتفوق على GPT-4o و Claude 3.5 في العديد من المهام!
257
  """
258
  )
259
+
260
  with gr.Row():
 
261
  with gr.Column(scale=3):
262
+ # اختيار نوع المدخل
263
+ mode = gr.Radio(
264
+ choices=["Text Only", "Image + Text", "Audio + Text", "Video + Text"],
265
+ value="Text Only",
266
+ label="Input Mode",
267
+ info="اختر نوع المدخل"
268
  )
269
+
270
+ # المدخلات
271
+ text_input = gr.Textbox(
272
+ label="Text Input",
273
+ placeholder="اكتب سؤالك أو النص هنا...",
274
+ lines=3
275
+ )
276
+
277
+ image_input = gr.Image(
278
+ label="Image Input",
279
+ type="pil",
280
+ visible=False
281
+ )
282
+
283
+ audio_input = gr.Audio(
284
+ label="Audio Input",
285
+ type="filepath",
286
+ visible=False
287
+ )
288
+
289
+ video_input = gr.Video(
290
+ label="Video Input",
291
+ visible=False
292
+ )
293
+
294
+ # زر الإرسال
295
+ submit_btn = gr.Button("🚀 Process", variant="primary")
296
+
297
+ # المخرجات
298
+ output_text = gr.Textbox(
299
+ label="Response",
300
+ lines=5,
301
+ interactive=False
302
+ )
303
+
304
+ output_audio = gr.Audio(
305
+ label="Generated Speech (TTS)",
306
  type="numpy",
307
+ visible=False
 
308
  )
309
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
  with gr.Column(scale=1):
311
+ gr.Markdown("### ⚙️ Settings")
312
+
313
+ temperature = gr.Slider(
314
+ label="Temperature",
315
+ minimum=0.0,
316
+ maximum=1.5,
317
+ value=0.7,
318
+ step=0.1
319
  )
320
+
321
+ top_p = gr.Slider(
322
+ label="Top-p",
323
+ minimum=0.1,
324
+ maximum=1.0,
325
+ value=0.9,
326
+ step=0.05
327
  )
328
+
329
+ max_new_tokens = gr.Slider(
330
+ label="Max Tokens",
331
+ minimum=50,
332
+ maximum=2048,
333
+ value=512,
334
+ step=50
335
  )
336
+
337
+ gr.Markdown("### 🗣️ TTS Settings")
338
+
339
+ enable_tts = gr.Checkbox(
340
+ label="Enable TTS",
341
+ value=False,
342
+ info="تحويل الرد إلى كلام"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
  )
344
+
345
+ tts_style = gr.Dropdown(
346
+ choices=["default", "emotional", "calm", "energetic"],
347
+ value="default",
348
+ label="TTS Style",
349
+ visible=False
350
+ )
351
+
352
+ # تحديث visibility حسب الوضع
353
+ def update_inputs(mode_value):
354
+ return {
355
+ image_input: gr.update(visible="Image" in mode_value),
356
+ audio_input: gr.update(visible="Audio" in mode_value),
357
+ video_input: gr.update(visible="Video" in mode_value),
358
+ }
359
+
360
+ mode.change(
361
+ fn=update_inputs,
362
+ inputs=[mode],
363
+ outputs=[image_input, audio_input, video_input]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  )
365
+
366
+ # تحديث visibility لإعدادات TTS
367
+ enable_tts.change(
368
+ fn=lambda x: {
369
+ tts_style: gr.update(visible=x),
370
+ output_audio: gr.update(visible=x)
371
+ },
372
+ inputs=[enable_tts],
373
+ outputs=[tts_style, output_audio]
 
 
374
  )
375
+
376
+ # معالجة الإرسال
377
+ submit_btn.click(
378
+ fn=minicpm_o_inference,
379
+ inputs=[
380
+ text_input,
381
+ image_input,
382
+ audio_input,
383
+ video_input,
384
+ mode,
385
+ temperature,
386
+ top_p,
387
+ max_new_tokens,
388
+ enable_tts,
389
+ tts_style
390
+ ],
391
+ outputs=[output_text, output_audio]
392
  )
393
+
394
+ # أمثلة
395
+ gr.Examples(
396
+ examples=[
397
+ ["What is artificial intelligence?", None, None, None, "Text Only"],
398
+ ["Describe this image in detail", "examples/sample.jpg", None, None, "Image + Text"],
399
+ ["Transcribe this audio", None, "examples/audio.wav", None, "Audio + Text"],
400
+ ["What happens in this video?", None, None, "examples/video.mp4", "Video + Text"],
401
+ ],
402
+ inputs=[text_input, image_input, audio_input, video_input, mode],
403
  )
404
+
405
  return demo
406
 
407
 
408
+ # =========================================================
409
+ # تشغيل التطبيق
410
+ # =========================================================
411
 
412
  if __name__ == "__main__":
413
+ demo = create_interface()
414
  demo.launch(
415
+ ssr_mode=False,
416
+ show_error=True,
417
+ share=False
418
  )