YussefGAFeer commited on
Commit
8c3fd4f
·
verified ·
1 Parent(s): c049bd7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +259 -189
app.py CHANGED
@@ -1,215 +1,285 @@
1
- import gradio as gr
2
- from transformers import AutoModelForCausalLM, AutoTokenizer, GenerationConfig
3
- import torch
4
- import gc
5
-
6
  # =============================================================================
7
- # إعداد النموذج (يحمّل مرة واحدة فقط عند بدء الخادم)
 
8
  # =============================================================================
9
 
10
- MODEL_ID = "WeiboAI/VibeThinker-1.5B"
 
 
 
 
 
 
11
 
12
- print(f"✅ [إعداد]: جاري تحميل النموذج {MODEL_ID}...")
13
- print(" - هذا قد يستغرق 1-3 دقائق أول مرة...")
 
 
 
14
 
 
 
 
15
  try:
16
- tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
17
- model = AutoModelForCausalLM.from_pretrained(
18
- MODEL_ID,
19
- torch_dtype=torch.bfloat16,
20
- device_map="auto",
21
- low_cpu_mem_usage=True,
22
- trust_remote_code=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  )
24
- print(" [جاهز]: تم تحميل النموذج بنجاح!")
 
 
 
 
 
 
 
 
 
 
25
  except Exception as e:
26
- print(f"❌ [خطأ]: فشل تحميل النموذج: {e}")
27
- raise
28
 
29
- # =============================================================================
30
- # دوال التوليد
31
- # =============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- def generate_response(prompt, temperature=0.6, max_tokens=2048):
34
- """توليد إجابة من النموذج"""
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  try:
36
- messages = [{"role": "user", "content": prompt}]
37
- text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
38
- inputs = tokenizer([text], return_tensors="pt").to(model.device)
39
-
40
- generation_config = {
41
- "max_new_tokens": max_tokens,
42
- "do_sample": True,
43
- "temperature": temperature,
44
- "top_p": 0.95,
45
- "top_k": None
46
- }
47
-
48
- with torch.no_grad():
49
- outputs = model.generate(**inputs, generation_config=GenerationConfig(**generation_config))
50
-
51
- response = tokenizer.batch_decode(outputs, skip_special_tokens=True)[0]
52
- # استخراج الإجابة فقط (إزالة Prompt)
53
- response = response.split("assistant\n")[-1].strip()
54
-
55
- return response
56
  except Exception as e:
57
- return f"❌ حدث خطأ: {str(e)}"
 
 
58
 
59
- def clear_memory():
60
- """تنظيف الذاكرة"""
61
- gc.collect()
62
- torch.cuda.empty_cache()
63
- return "✅ تم تنظيف الذاكرة"
64
 
65
- # =============================================================================
66
- # واجهة Gradio
67
- # =============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- css = """
70
- /* تنسيق مشابه للـ Colab */
71
- body { background: linear-gradient(135deg, #1a1a2e, #16213e); color: white; }
72
- .gradio-container { background: rgba(0,0,0,0.3); border-radius: 15px; }
 
 
 
 
 
 
 
73
  """
74
 
75
- with gr.Blocks(title="VibeThinker API | HF Space", css=css, theme=gr.themes.Soft()) as demo:
76
-
77
- gr.Markdown("""
78
- # 🚀 VibeThinker-1.5B API على Hugging Face Space
79
-
80
- **نموذج التفكير المنطقي والرياضيات من WeiboAI**
81
-
82
- ⚡ **النموذج**: `WeiboAI/VibeThinker-1.5B`
83
- 🔗 **المصدر**: [Hugging Face Hub](https://huggingface.co/WeiboAI/VibeThinker-1.5B)
84
- 📄 **الترخيص**: MIT (مجاني للاستخدام التجاري)
85
- """)
86
 
87
  with gr.Row():
88
- with gr.Column(scale=4):
89
- chatbot = gr.Chatbot(
90
- label="💬 محادثة",
91
- height=500,
92
- bubble_full_width=False,
93
- avatar_images=(None, "https://huggingface.co/WeiboAI/VibeThinker-1.5B/resolve/main/logo.png")
94
- )
95
-
96
- with gr.Row():
97
- prompt_input = gr.Textbox(
98
- label="اكتب سؤالك هنا...",
99
- placeholder="أدخل مسألة رياضية أو سؤال منطقي...",
100
- lines=2,
101
- scale=8
102
- )
103
- submit_btn = gr.Button("🚀 إرسال", scale=1, variant="primary")
104
-
105
- with gr.Accordion("⚙️ إعدادات متقدمة", open=False):
106
- temperature = gr.Slider(0.1, 1.5, value=0.6, label="Temperature", info="درجة الإبداع")
107
- max_tokens = gr.Slider(512, 8192, value=2048, label="Max Tokens", info="أقصى طول للإجابة")
108
- clear_btn = gr.Button("🗑️ مسح الذاكرة", variant="stop")
109
-
110
- with gr.Column(scale=1):
111
- gr.Markdown("### 📋 معلومات")
112
- gr.Markdown("""
113
- - **سرعة الاستجابة**: ~2-5 ثانية/سؤال
114
- - **دعم اللغة**: الإنجليزية (المهام الرياضية)
115
- - **استخدام الذاكرة**: ~4GB VRAM
116
- """)
117
-
118
- status_box = gr.Textbox(
119
- label="حالة النظام",
120
- value="✅ النموذج جاهز",
121
- interactive=False
122
- )
123
 
124
- # =============================================================================
125
- # أحداث التحكم
126
- # =============================================================================
127
-
128
- def chat_function(message, history, temp, max_tok):
129
- """معالجة المحادثة"""
130
- # بناء السياق من المحادثة السابقة
131
- full_prompt = ""
132
- for human, assistant in history:
133
- full_prompt += f"Human: {human}\nAssistant: {assistant}\n"
134
- full_prompt += f"Human: {message}\nAssistant: "
135
-
136
- response = generate_response(full_prompt, temp, max_tok)
137
- return response
138
-
139
- def handle_submit(prompt, history, temp, max_tok):
140
- """معالجة إرسال السؤال"""
141
- if not prompt.strip():
142
- return "", history
143
-
144
- # إضافة السؤال للمحادثة
145
- history.append([prompt, None])
146
-
147
- # توليد الإجابة
148
- response = chat_function(prompt, history[:-1], temp, max_tok)
149
-
150
- # تحديث المحادثة بالإجابة
151
- history[-1][1] = response
152
-
153
- # تنظيف الذاكرة بعد كل استجابة
154
- clear_memory()
155
-
156
- return "", history
157
-
158
- # ربط الأحداث
159
- submit_btn.click(
160
- handle_submit,
161
- inputs=[prompt_input, chatbot, temperature, max_tokens],
162
- outputs=[prompt_input, chatbot]
163
- )
164
-
165
- prompt_input.submit(
166
- handle_submit,
167
- inputs=[prompt_input, chatbot, temperature, max_tokens],
168
- outputs=[prompt_input, chatbot]
169
- )
170
-
171
- clear_btn.click(
172
- clear_memory,
173
- outputs=[status_box]
174
- )
175
-
176
- # =============================================================================
177
- # معلومات مخصصة لـ API
178
- # =============================================================================
179
 
180
  gr.Markdown("""
181
  ---
182
- ### 🔌 **استخدام كـ API**
183
-
184
- يمكنك استخدام هذا Space كـ API خارجي:
185
-
186
- **Endpoint**: `https://YOUR-SPACE-NAME.hf.space/v1/chat/completions`
187
-
188
- **Headers**:
189
- ```json
190
- {
191
- "Content-Type": "application/json"
192
- }
193
- ```
194
-
195
- **Body**:
196
- ```json
197
- {
198
- "model": "VibeThinker-1.5B",
199
- "messages": [{"role": "user", "content": "مسألتك"}],
200
- "temperature": 0.6
201
- }
202
- ```
203
  """)
204
 
205
- # =============================================================================
206
- # تشغيل التطبيق
207
- # =============================================================================
208
 
209
- demo.queue(max_size=20).launch(
210
- server_name="0.0.0.0",
211
- server_port=7860,
212
- share=False, # لا تحتاج لـ share لأن Space عام
213
- show_api=True, # إظهار وثائق API تلقائياً
214
- show_error=True
215
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # =============================================================================
2
+ # كود Ollama لـ Hugging Face Space مع Ngrok
3
+ # نسخة محسّنة ومستقرة
4
  # =============================================================================
5
 
6
+ import sys
7
+ import subprocess
8
+ import time
9
+ import os
10
+ import signal
11
+ import threading
12
+ import gradio as gr
13
 
14
+ # -----------------------------------------------------------------------------
15
+ # الجزء الأول: تثبيت المكتبات الضرورية و Ollama
16
+ # -----------------------------------------------------------------------------
17
+ print("✅ [الخطوة 1/5]: تثبيت المكتبات الضرورية و Ollama...")
18
+ sys.stdout.flush()
19
 
20
+ # تثبيت pyngrok
21
+ print(" - تثبيت pyngrok...", end="")
22
+ sys.stdout.flush()
23
  try:
24
+ subprocess.run([sys.executable, '-m', 'pip', 'install', 'pyngrok', '-q'], check=True)
25
+ print(" تم.")
26
+ sys.stdout.flush()
27
+ except subprocess.CalledProcessError as e:
28
+ print(f"\n[❌ خطأ فادح]: فشل تثبيت pyngrok. رمز الخروج: {e.returncode}")
29
+ sys.exit(1)
30
+
31
+ from pyngrok import ngrok
32
+
33
+ # تثبيت Ollama
34
+ print(" - تثبيت Ollama بالطريقة الرسمية...")
35
+ sys.stdout.flush()
36
+ try:
37
+ install_command = "curl -fsSL https://ollama.com/install.sh | sh"
38
+ subprocess.run(install_command, shell=True, check=True, capture_output=True, text=True)
39
+ print(" - تم تثبيت Ollama بنجاح.")
40
+ sys.stdout.flush()
41
+ except subprocess.CalledProcessError as e:
42
+ print(f"\n[❌ خطأ فادح]: فشل تثبيت Ollama. رمز الخروج: {e.returncode}")
43
+ print(e.stderr)
44
+ sys.exit(1)
45
+ except Exception as e:
46
+ print(f"\n[❌ خطأ فادح]: حدث خطأ غير متوقع أثناء تثبيت Ollama. الخطأ: {e}")
47
+ sys.exit(1)
48
+
49
+ print("✅ [الخطوة 1/5]: تم تثبيت المكتبات و Ollama بنجاح!\n")
50
+ sys.stdout.flush()
51
+
52
+ # -----------------------------------------------------------------------------
53
+ # الجزء الثاني: إعداد Ngrok وتشغيل خادم Ollama
54
+ # -----------------------------------------------------------------------------
55
+ print("✅ [الخطوة 2/5]: إعداد Ngrok وتشغيل خادم Ollama...")
56
+ sys.stdout.flush()
57
+
58
+ # استخدم Hugging Face Secrets لتخزين التوكن بشكل آمن
59
+ NGROK_AUTH_TOKEN = os.getenv("NGROK_AUTH_TOKEN", "")
60
+
61
+ try:
62
+ if not NGROK_AUTH_TOKEN:
63
+ raise ValueError("⚠️ يرجى إضافة NGROK_AUTH_TOKEN في إعدادات Secrets للـ Space")
64
+ print(" - إعداد Ngrok Auth Token...", end="")
65
+ ngrok.set_auth_token(NGROK_AUTH_TOKEN)
66
+ print(" تم.")
67
+ sys.stdout.flush()
68
+ except Exception as e:
69
+ print(f"\n[❌ خطأ فادح]: فشل إعداد Ngrok Auth Token. الخطأ: {e}")
70
+ sys.exit(1)
71
+
72
+ # تشغيل خادم Ollama في الخلفية
73
+ print(" - تشغيل خادم Ollama في الخلفية...", end="")
74
+ sys.stdout.flush()
75
+ os.environ['OLLAMA_HOST'] = '0.0.0.0:11434'
76
+
77
+ ollama_serve_process = None
78
+ try:
79
+ ollama_serve_process = subprocess.Popen(
80
+ ['ollama', 'serve'],
81
+ stdout=subprocess.PIPE,
82
+ stderr=subprocess.PIPE,
83
+ preexec_fn=os.setsid
84
  )
85
+ print(" جاري البدء...", end="")
86
+ time.sleep(10)
87
+
88
+ check_process = subprocess.run(['ollama', 'list'], capture_output=True, text=True, timeout=20)
89
+ if check_process.returncode == 0:
90
+ print(" يعمل بنجاح.")
91
+ else:
92
+ print(f"\n - [❌] فشل تشغيل خادم Ollama بشكل صحيح.")
93
+ stderr_output = ollama_serve_process.stderr.read().decode('utf-8')
94
+ print(f" الخطأ من الخادم: {stderr_output.strip()}")
95
+ raise Exception("الخادم لم يبدأ بشكل صحيح.")
96
  except Exception as e:
97
+ print(f"\n[❌ خطأ فادح]: فشل تشغيل خادم Ollama. الخطأ: {e}")
98
+ sys.exit(1)
99
 
100
+ print("✅ [الخطوة 2/5]: تم تشغيل خادم Ollama بنجاح.\n")
101
+ sys.stdout.flush()
102
+
103
+ # -----------------------------------------------------------------------------
104
+ # الجزء الثالث: تشغيل ngrok وإنشاء الرابط العام
105
+ # -----------------------------------------------------------------------------
106
+ print("✅ [الخطوة 3/5]: جاري تشغيل ngrok لتعريض Ollama API...")
107
+ sys.stdout.flush()
108
+
109
+ ngrok_tunnel = None
110
+ public_url_str = None
111
+
112
+ try:
113
+ print(" - إنشاء نفق ngrok...", end="")
114
+ ngrok_tunnel = ngrok.connect(11434, "http")
115
+ public_url_str = ngrok_tunnel.public_url
116
+ print(" تم.")
117
+ sys.stdout.flush()
118
+
119
+ print("\n🔗 الـ API متاح الآن على الرابط العام التالي:")
120
+ print(f" {public_url_str}")
121
+ print("\n")
122
+ sys.stdout.flush()
123
+
124
+ except Exception as e:
125
+ print(f"\n[❌ خطأ فادح]: حدث خطأ أثناء تشغيل ngrok. الخطأ: {e}")
126
+ sys.exit(1)
127
+
128
+ print("✅ [الخطوة 3/5]: تم تشغيل النفق بنجاح وتم عرض الرابط.\n")
129
+ sys.stdout.flush()
130
+
131
+ # -----------------------------------------------------------------------------
132
+ # الجزء الرابع: فحص مساحة التخزين وسحب النماذج
133
+ # -----------------------------------------------------------------------------
134
+ print("✅ [الخطوة 4/5]: فحص مساحة التخزين وسحب النماذج المطلوبة...")
135
+ sys.stdout.flush()
136
+
137
+ print(" - فحص مساحة التخزين المتاحة...")
138
+ subprocess.run(['df', '-h', '/'])
139
+ print("")
140
+ sys.stdout.flush()
141
 
142
+ models_to_pull = [
143
+ "hf.co/Mungert/VibeThinker-1.5B-GGUF:BF16",
144
+ "hf.co/Mungert/Qwen3-30B-A1.5B-High-Speed-GGUF:IQ3_M",
145
+ ]
146
+
147
+ successfully_pulled = []
148
+ failed_to_pull = []
149
+
150
+ print(" - ستبدأ عملية سحب النماذج بشكل تسلسلي (واحد تلو الآخر) لضمان الاستقرار.")
151
+ print(" - ستظهر لك نسبة التقدم لكل نموذج بشكل مباشر. قد يستغرق هذا وقتًا طويلاً.\n")
152
+ sys.stdout.flush()
153
+
154
+ for model_name in models_to_pull:
155
+ print(f"--- [⏳] جاري سحب النموذج: {model_name} ---")
156
+ sys.stdout.flush()
157
  try:
158
+ subprocess.run(
159
+ ['ollama', 'pull', model_name],
160
+ check=True
161
+ )
162
+ print(f"--- [✔️] تم سحب النموذج {model_name} بنجاح ---\n")
163
+ successfully_pulled.append(model_name)
164
+ except subprocess.CalledProcessError as e:
165
+ print(f"--- [❌] فشل سحب النموذج {model_name}. رمز الخروج: {e.returncode} ---\n")
166
+ failed_to_pull.append(model_name)
 
 
 
 
 
 
 
 
 
 
 
167
  except Exception as e:
168
+ print(f"--- [] حدث خطأ استثنائي عند سحب النموذج {model_name}. الخطأ: {e} ---\n")
169
+ failed_to_pull.append(model_name)
170
+ sys.stdout.flush()
171
 
172
+ print("✅ [الخطوة 4/5]: انتهت عملية سحب النماذج!\n")
 
 
 
 
173
 
174
+ print("--- ملخص عملية السحب ---")
175
+ if successfully_pulled:
176
+ print(f"✔️ نماذج تم سحبها بنجاح ({len(successfully_pulled)}): {', '.join(successfully_pulled)}")
177
+ if failed_to_pull:
178
+ print(f"❌ نماذج فشل سحبها ({len(failed_to_pull)}): {', '.join(failed_to_pull)}")
179
+ print("-------------------------\n")
180
+ sys.stdout.flush()
181
+
182
+ # -----------------------------------------------------------------------------
183
+ # الجزء الخامس: إنشاء واجهة Gradio
184
+ # -----------------------------------------------------------------------------
185
+ print("✅ [الخطوة 5/5]: إنشاء واجهة Gradio...")
186
+
187
+ def get_models_list():
188
+ """الحصول على قائمة النماذج المثبتة"""
189
+ try:
190
+ result = subprocess.run(['ollama', 'list'], capture_output=True, text=True, timeout=10)
191
+ return result.stdout
192
+ except Exception as e:
193
+ return f"❌ خطأ في الحصول على قائمة النماذج: {e}"
194
+
195
+ # إنشاء محتوى تعليمات الاستخدام
196
+ instructions = f"""
197
+ # ✨ خادم Ollama جاهز للاستخدام ✨
198
+
199
+ ## 🔗 رابط API العام:
200
+ ```
201
+ {public_url_str}
202
+ ```
203
+
204
+ ## 📋 تعليمات الاستخدام في RikkaHub:
205
+
206
+ 1. انسخ الرابط أعلاه
207
+ 2. في RikkaHub، اذهب إلى إعدادات المزود (Provider Settings)
208
+ 3. اختر نوع المزود 'OpenAI-Compatible'
209
+ 4. في خانة 'Base URL'، الصق الرابط وأضف له /v1:
210
+ ```
211
+ {public_url_str}/v1
212
+ ```
213
+ 5. في خانة 'Model'، اكتب اسم النموذج الذي تريد استخدامه
214
 
215
+ ## 📊 قائمة النماذج المثبتة:
216
+
217
+ ```
218
+ {get_models_list()}
219
+ ```
220
+
221
+ ## ⚠️ ملاحظات مهمة:
222
+
223
+ - يجب إبقاء هذا Space قيد التشغيل للحفاظ على الاتصال
224
+ - الرابط العام سيتغير إذا أعيد تشغيل Space
225
+ - استخدم النماذج الخفيفة للحصول على أداء أفضل
226
  """
227
 
228
+ # إنشاء واجهة Gradio
229
+ with gr.Blocks(title="Ollama Server on Hugging Face", theme=gr.themes.Soft()) as demo:
230
+ gr.Markdown(instructions)
 
 
 
 
 
 
 
 
231
 
232
  with gr.Row():
233
+ refresh_btn = gr.Button("🔄 تحديث قائمة النماذج", size="sm")
234
+ models_output = gr.Textbox(
235
+ label="النماذج المثبتة حاليًا",
236
+ value=get_models_list(),
237
+ lines=10,
238
+ interactive=False
239
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
+ refresh_btn.click(fn=get_models_list, outputs=models_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  gr.Markdown("""
244
  ---
245
+ ### 💡 نصائح للاستخدام الأمثل:
246
+ - استخدم النماذج الصغيرة (1B-3B) للحصول على استجابة سريعة
247
+ - تأكد من استقرار الاتصال بالإنترنت
248
+ - راقب استخدام الذاكرة في Logs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  """)
250
 
251
+ print("✅ تم إنشاء واجهة Gradio بنجاح!")
252
+ print("⏳ الخادم يعمل الآن ومتاح للاستخدام...")
253
+ sys.stdout.flush()
254
 
255
+ # إطلاق Gradio في thread منفصل للسماح بالتشغيل المستمر
256
+ if __name__ == "__main__":
257
+ try:
258
+ demo.launch(
259
+ server_name="0.0.0.0",
260
+ server_port=7860,
261
+ share=False,
262
+ show_error=True
263
+ )
264
+ except KeyboardInterrupt:
265
+ print('\n\n⏳ تم طلب الإيقاف. جاري إغلاق العمليات...')
266
+ finally:
267
+ if ollama_serve_process:
268
+ try:
269
+ pgid = os.getpgid(ollama_serve_process.pid)
270
+ os.killpg(pgid, signal.SIGTERM)
271
+ print(" - تم إيقاف خادم Ollama.")
272
+ except OSError:
273
+ print(" - ⚠️ لم يتم العثور على عملية خادم Ollama.")
274
+ if ngrok_tunnel:
275
+ try:
276
+ ngrok.disconnect(public_url_str)
277
+ print(" - تم إيقاف نفق ngrok.")
278
+ except Exception as e:
279
+ print(f" - ⚠️ فشل إيقاف ngrok: {e}")
280
+ try:
281
+ ngrok.kill()
282
+ print(" - تم إيقاف ngrok daemon.")
283
+ except Exception as e:
284
+ print(f" - ⚠️ فشل إيقاف ngrok daemon: {e}")
285
+ print('✅ تم إيقاف جميع العمليات بنجاح.')