YussefGAFeer commited on
Commit
35ff464
·
verified ·
1 Parent(s): 71561b1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +266 -124
app.py CHANGED
@@ -1,6 +1,5 @@
1
  # =============================================================================
2
- # كود Ollama لـ Hugging Face Space مع Ngrok
3
- # نسخة محسّنة ومستقرة
4
  # =============================================================================
5
 
6
  import sys
@@ -8,74 +7,74 @@ 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,
@@ -83,62 +82,130 @@ try:
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",
@@ -147,8 +214,7 @@ models_to_pull = [
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:
@@ -157,19 +223,23 @@ for model_name in models_to_pull:
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:
@@ -180,79 +250,152 @@ 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(
@@ -262,24 +405,23 @@ if __name__ == "__main__":
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('✅ تم إيقاف جميع العمليات بنجاح.')
 
1
  # =============================================================================
2
+ # كود Ollama لـ Hugging Face Space مع Ngrok (نسخة محسّنة ضد Timeout)
 
3
  # =============================================================================
4
 
5
  import sys
 
7
  import time
8
  import os
9
  import signal
 
10
  import gradio as gr
11
+ import socket
12
 
13
  # -----------------------------------------------------------------------------
14
  # الجزء الأول: تثبيت المكتبات الضرورية و Ollama
15
  # -----------------------------------------------------------------------------
16
+ print("✅ [الخطوة 1/6]: تثبيت المكتبات الضرورية و Ollama...")
17
  sys.stdout.flush()
18
 
19
+ # تثبيت pyngrok بمحاولات متعددة
20
  print(" - تثبيت pyngrok...", end="")
21
  sys.stdout.flush()
22
+ max_retries = 3
23
+ for attempt in range(max_retries):
24
+ try:
25
+ subprocess.run(
26
+ [sys.executable, '-m', 'pip', 'install', 'pyngrok', '-q', '--no-cache-dir'],
27
+ check=True,
28
+ timeout=120
29
+ )
30
+ print(" تم.")
31
+ sys.stdout.flush()
32
+ break
33
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
34
+ if attempt < max_retries - 1:
35
+ print(f" فشل (محاولة {attempt + 1}/{max_retries})، إعادة المحاولة...")
36
+ time.sleep(5)
37
+ else:
38
+ print(f"\n[❌ خطأ فادح]: فشل تثبيت pyngrok بعد {max_retries} محاولات.")
39
+ sys.exit(1)
40
 
41
  # تثبيت Ollama
42
  print(" - تثبيت Ollama بالطريقة الرسمية...")
43
  sys.stdout.flush()
44
  try:
45
  install_command = "curl -fsSL https://ollama.com/install.sh | sh"
46
+ result = subprocess.run(
47
+ install_command,
48
+ shell=True,
49
+ check=True,
50
+ capture_output=True,
51
+ text=True,
52
+ timeout=300
53
+ )
54
  print(" - تم تثبيت Ollama بنجاح.")
55
  sys.stdout.flush()
56
+ except subprocess.TimeoutExpired:
57
+ print("\n[❌ خطأ فادح]: انتهت مهلة تثبيت Ollama (300 ثانية).")
58
+ sys.exit(1)
59
  except subprocess.CalledProcessError as e:
60
  print(f"\n[❌ خطأ فادح]: فشل تثبيت Ollama. رمز الخروج: {e.returncode}")
61
  print(e.stderr)
62
  sys.exit(1)
 
 
 
63
 
64
+ print("✅ [الخطوة 1/6]: تم تثبيت المكتبات و Ollama بنجاح!\n")
65
  sys.stdout.flush()
66
 
67
  # -----------------------------------------------------------------------------
68
+ # الجزء الثاني: تشغيل خادم Ollama أولاً
69
  # -----------------------------------------------------------------------------
70
+ print("✅ [الخطوة 2/6]: تشغيل خادم Ollama...")
71
  sys.stdout.flush()
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  os.environ['OLLAMA_HOST'] = '0.0.0.0:11434'
74
 
75
  ollama_serve_process = None
76
  try:
77
+ print(" - تشغيل خادم Ollama في الخلفية...", end="")
78
  ollama_serve_process = subprocess.Popen(
79
  ['ollama', 'serve'],
80
  stdout=subprocess.PIPE,
 
82
  preexec_fn=os.setsid
83
  )
84
  print(" جاري البدء...", end="")
85
+ time.sleep(15)
86
 
87
+ check_process = subprocess.run(
88
+ ['ollama', 'list'],
89
+ capture_output=True,
90
+ text=True,
91
+ timeout=30
92
+ )
93
  if check_process.returncode == 0:
94
  print(" يعمل بنجاح.")
95
  else:
96
  print(f"\n - [❌] فشل تشغيل خادم Ollama بشكل صحيح.")
97
+ if ollama_serve_process.stderr:
98
+ stderr_output = ollama_serve_process.stderr.read().decode('utf-8')
99
+ print(f" الخطأ من الخادم: {stderr_output.strip()}")
100
  raise Exception("الخادم لم يبدأ بشكل صحيح.")
101
  except Exception as e:
102
  print(f"\n[❌ خطأ فادح]: فشل تشغيل خادم Ollama. الخطأ: {e}")
103
  sys.exit(1)
104
 
105
+ print("✅ [الخطوة 2/6]: تم تشغيل خادم Ollama بنجاح.\n")
106
  sys.stdout.flush()
107
 
108
  # -----------------------------------------------------------------------------
109
+ # الجزء الثالث: إعداد Ngrok مع معالجة محسّنة للـ Timeout
110
  # -----------------------------------------------------------------------------
111
+ print("✅ [الخطوة 3/6]: إعداد Ngrok مع حماية من Timeout...")
112
  sys.stdout.flush()
113
 
114
+ NGROK_AUTH_TOKEN = os.getenv("NGROK_AUTH_TOKEN", "")
115
+
116
  ngrok_tunnel = None
117
  public_url_str = None
118
+ ngrok_setup_success = False
119
 
120
+ if not NGROK_AUTH_TOKEN:
121
+ print("⚠️ [تحذير]: لم يتم العثور على NGROK_AUTH_TOKEN في Secrets")
122
+ print(" سيتم تخطي إعداد Ngrok. يمكنك استخدام الخادم محلياً فقط.")
123
+ print(" لإضافة التوكن: Settings → Repository secrets → NGROK_AUTH_TOKEN\n")
124
+ else:
125
+ try:
126
+ print(" - استيراد pyngrok...", end="")
127
+ import pyngrok
128
+ from pyngrok import ngrok, conf
129
+ print(" تم.")
130
+
131
+ # زيادة timeout لـ ngrok
132
+ print(" - تكوين إعدادات ngrok...", end="")
133
+ pyngrok_config = conf.get_default()
134
+ pyngrok_config.start_timeout = 180 # 3 دقائق
135
+ pyngrok_config.request_timeout = 30
136
+ print(" تم.")
137
+
138
+ print(" - تعيين Auth Token...", end="")
139
+ ngrok.set_auth_token(NGROK_AUTH_TOKEN)
140
+ print(" تم.")
141
+
142
+ print(" - إنشاء نفق ngrok (قد يستغرق دقائق)...", end="")
143
+ sys.stdout.flush()
144
+
145
+ # محاولة الاتصال مع retry
146
+ for attempt in range(3):
147
+ try:
148
+ ngrok_tunnel = ngrok.connect(
149
+ 11434,
150
+ "http",
151
+ bind_tls=True
152
+ )
153
+ public_url_str = ngrok_tunnel.public_url
154
+ ngrok_setup_success = True
155
+ print(" تم بنجاح!")
156
+ break
157
+ except Exception as conn_error:
158
+ if attempt < 2:
159
+ print(f"\n محاولة {attempt + 1} فشلت، إعادة المحاولة بعد 10 ثواني...")
160
+ time.sleep(10)
161
+ else:
162
+ raise conn_error
163
+
164
+ if ngrok_setup_success:
165
+ print(f"\n🔗 الـ API متاح الآن على الرابط العام التالي:")
166
+ print(f" {public_url_str}\n")
167
+ sys.stdout.flush()
168
+
169
+ except ImportError as e:
170
+ print(f"\n⚠️ [تحذير]: فشل استيراد pyngrok: {e}")
171
+ print(" سيتم تشغيل الخادم محلياً فقط.\n")
172
+ except Exception as e:
173
+ error_msg = str(e)
174
+ if "timeout" in error_msg.lower() or "timed out" in error_msg.lower():
175
+ print(f"\n⚠️ [تحذير]: انتهت مهلة الاتصال بـ ngrok: {error_msg}")
176
+ print(" الأسباب المحتملة:")
177
+ print(" 1. اتصال الإنترنت بطيء في Hugging Face Space")
178
+ print(" 2. خوادم ngrok مشغولة")
179
+ print(" 3. جدار حماية يمنع الاتصال")
180
+ print("\n الحلول:")
181
+ print(" ✓ أعد تش��يل Space")
182
+ print(" ✓ استخدم Cloudflare Tunnel بدلاً من ngrok")
183
+ print(" ✓ استخدم الخادم محلياً داخل Hugging Face\n")
184
+ else:
185
+ print(f"\n⚠️ [تحذير]: فشل إعداد Ngrok: {e}")
186
+ print(" سيتم تشغيل الخادم محلياً فقط.\n")
187
+
188
+ if ngrok_setup_success:
189
+ print("✅ [الخطوة 3/6]: تم تشغيل النفق بنجاح!\n")
190
+ else:
191
+ print("⚠️ [الخطوة 3/6]: تم تخطي Ngrok، الخادم يعمل محلياً فقط.\n")
192
  sys.stdout.flush()
193
 
194
  # -----------------------------------------------------------------------------
195
+ # الجزء الرابع: فحص مساحة التخزين
196
  # -----------------------------------------------------------------------------
197
+ print("✅ [الخطوة 4/6]: فحص مساحة التخزين...")
198
  sys.stdout.flush()
 
 
199
  subprocess.run(['df', '-h', '/'])
200
  print("")
201
  sys.stdout.flush()
202
 
203
+ # -----------------------------------------------------------------------------
204
+ # الجزء الخامس: سحب النماذج
205
+ # -----------------------------------------------------------------------------
206
+ print("✅ [الخطوة 5/6]: سحب النماذج المطلوبة...")
207
+ sys.stdout.flush()
208
+
209
  models_to_pull = [
210
  "hf.co/Mungert/VibeThinker-1.5B-GGUF:BF16",
211
  "hf.co/Mungert/Qwen3-30B-A1.5B-High-Speed-GGUF:IQ3_M",
 
214
  successfully_pulled = []
215
  failed_to_pull = []
216
 
217
+ print(" - بدء سحب النماذج بشكل تسلسلي...\n")
 
218
  sys.stdout.flush()
219
 
220
  for model_name in models_to_pull:
 
223
  try:
224
  subprocess.run(
225
  ['ollama', 'pull', model_name],
226
+ check=True,
227
+ timeout=1800 # 30 دقيقة لكل نموذج
228
  )
229
  print(f"--- [✔️] تم سحب النموذج {model_name} بنجاح ---\n")
230
  successfully_pulled.append(model_name)
231
+ except subprocess.TimeoutExpired:
232
+ print(f"--- [❌] انتهت مهلة سحب النموذج {model_name} ---\n")
233
+ failed_to_pull.append(model_name)
234
  except subprocess.CalledProcessError as e:
235
  print(f"--- [❌] فشل سحب النموذج {model_name}. رمز الخروج: {e.returncode} ---\n")
236
  failed_to_pull.append(model_name)
237
  except Exception as e:
238
+ print(f"--- [❌] خطأ استثنائي: {e} ---\n")
239
  failed_to_pull.append(model_name)
240
  sys.stdout.flush()
241
 
242
+ print("✅ [الخطوة 5/6]: انتهت عملية سحب النماذج!\n")
243
 
244
  print("--- ملخص عملية السحب ---")
245
  if successfully_pulled:
 
250
  sys.stdout.flush()
251
 
252
  # -----------------------------------------------------------------------------
253
+ # الجزء السادس: إنشاء واجهة Gradio
254
  # -----------------------------------------------------------------------------
255
+ print("✅ [الخطوة 6/6]: إنشاء واجهة Gradio...")
256
 
257
  def get_models_list():
258
  """الحصول على قائمة النماذج المثبتة"""
259
  try:
260
+ result = subprocess.run(
261
+ ['ollama', 'list'],
262
+ capture_output=True,
263
+ text=True,
264
+ timeout=10
265
+ )
266
+ return result.stdout if result.returncode == 0 else "❌ خطأ في الحصول على القائمة"
267
  except Exception as e:
268
+ return f"❌ خطأ: {e}"
269
+
270
+ def get_server_status():
271
+ """الحصول على حالة الخادم"""
272
+ status = "🟢 الخادم يعمل\n\n"
273
+
274
+ # حالة Ollama
275
+ try:
276
+ result = subprocess.run(['ollama', 'list'], capture_output=True, timeout=5)
277
+ if result.returncode == 0:
278
+ status += "✅ Ollama: يعمل\n"
279
+ else:
280
+ status += "❌ Ollama: لا يعمل\n"
281
+ except:
282
+ status += "❌ Ollama: خطأ في الفحص\n"
283
+
284
+ # حالة Ngrok
285
+ if ngrok_setup_success and public_url_str:
286
+ status += f"✅ Ngrok: متصل\n"
287
+ status += f"🔗 الرابط العام: {public_url_str}\n"
288
+ else:
289
+ status += "⚠️ Ngrok: غير متصل (محلي فقط)\n"
290
+
291
+ return status
292
+
293
+ # إنشاء محتوى الواجهة
294
+ if ngrok_setup_success and public_url_str:
295
+ api_url = public_url_str
296
+ api_url_v1 = f"{public_url_str}/v1"
297
+ connection_status = "🟢 متصل بالإنترنت عبر Ngrok"
298
+ else:
299
+ api_url = "http://localhost:11434"
300
+ api_url_v1 = "http://localhost:11434/v1"
301
+ connection_status = "🟡 يعمل محلياً فقط (داخل Hugging Face Space)"
302
 
 
303
  instructions = f"""
304
+ # ✨ خادم Ollama على Hugging Face Space
305
+
306
+ ## 📊 حالة الاتصال
307
+ {connection_status}
308
+
309
+ ## 🔗 روابط API:
310
 
311
+ ### الرابط الأساسي:
312
  ```
313
+ {api_url}
314
  ```
315
 
316
+ ### رابط OpenAI Compatible:
317
+ ```
318
+ {api_url_v1}
319
+ ```
320
 
321
+ ## 📋 تعليمات الاستخدام:
 
 
 
 
 
 
 
322
 
323
+ ### في RikkaHub (إذا كان Ngrok يعمل):
324
+ 1. انسخ الرابط: `{api_url_v1}`
325
+ 2. اذهب إلى Provider Settings
326
+ 3. اختر: OpenAI-Compatible
327
+ 4. Base URL: الصق الرابط
328
+ 5. Model: اسم النموذج (مثال: `hf.co/Mungert/VibeThinker-1.5B-GGUF:BF16`)
329
 
330
+ ### اختبار باستخدام curl:
331
+ ```bash
332
+ curl {api_url}/api/tags
333
  ```
 
 
 
 
334
 
335
+ ## ⚠️ ملاحظات:
336
+ - إذا فشل Ngrok، يمكنك استخدام الخادم محلياً داخل HF فقط
337
+ - أعد تشغيل Space إذا واجهت مشاكل
338
+ - استخدم نماذج صغيرة للأداء الأفضل
339
  """
340
 
341
  # إنشاء واجهة Gradio
342
+ with gr.Blocks(title="Ollama Server", theme=gr.themes.Soft(), css="""
343
+ .status-box { padding: 15px; border-radius: 8px; background: #f0f0f0; }
344
+ .warning-box { padding: 15px; border-radius: 8px; background: #fff3cd; border-left: 4px solid #ffc107; }
345
+ .success-box { padding: 15px; border-radius: 8px; background: #d4edda; border-left: 4px solid #28a745; }
346
+ """) as demo:
347
+
348
  gr.Markdown(instructions)
349
 
350
  with gr.Row():
351
+ with gr.Column(scale=1):
352
+ status_btn = gr.Button("🔄 تحديث حالة الخادم", size="sm")
353
+ status_output = gr.Textbox(
354
+ label="حالة الخادم",
355
+ value=get_server_status(),
356
+ lines=6,
357
+ interactive=False
358
+ )
359
+
360
+ with gr.Column(scale=1):
361
+ models_btn = gr.Button("🔄 تحديث قائمة النماذج", size="sm")
362
+ models_output = gr.Textbox(
363
+ label="النماذج المثبتة",
364
+ value=get_models_list(),
365
+ lines=6,
366
+ interactive=False
367
+ )
368
+
369
+ status_btn.click(fn=get_server_status, outputs=status_output)
370
+ models_btn.click(fn=get_models_list, outputs=models_output)
371
 
372
+ if not ngrok_setup_success:
373
+ gr.Markdown("""
374
+ <div class="warning-box">
375
+ <h3>⚠️ تنبيه: Ngrok غير متصل</h3>
376
+ <p>فشل الاتصال بـ Ngrok بسبب timeout. الخادم يعمل محلياً فقط.</p>
377
+ <p><strong>الحلول الممكنة:</strong></p>
378
+ <ul>
379
+ <li>أعد تشغيل Space (قد ينجح في المحاولة الثانية)</li>
380
+ <li>تحقق من إضافة NGROK_AUTH_TOKEN في Secrets بشكل صحيح</li>
381
+ <li>استخدم بديل مثل Cloudflare Tunnel</li>
382
+ </ul>
383
+ </div>
384
+ """)
385
 
386
  gr.Markdown("""
387
  ---
388
+ ### 💡 نصائح:
389
+ - النماذج الصغيرة (1B-3B) أسرع على CPU
 
390
  - راقب استخدام الذاكرة في Logs
391
+ - أعد تشغيل Space إذا حدثت مشاكل
392
  """)
393
 
394
  print("✅ تم إنشاء واجهة Gradio بنجاح!")
395
+ print("⏳ الخادم جاهز للاستخدام...\n")
396
  sys.stdout.flush()
397
 
398
+ # إطلاق Gradio
399
  if __name__ == "__main__":
400
  try:
401
  demo.launch(
 
405
  show_error=True
406
  )
407
  except KeyboardInterrupt:
408
+ print('\n⏳ إيقاف...')
409
  finally:
410
+ # تنظيف العمليات
411
  if ollama_serve_process:
412
  try:
413
  pgid = os.getpgid(ollama_serve_process.pid)
414
  os.killpg(pgid, signal.SIGTERM)
415
+ print(" تم إيقاف Ollama")
416
+ except:
417
+ pass
418
+
419
  if ngrok_tunnel:
420
  try:
421
  ngrok.disconnect(public_url_str)
422
+ ngrok.kill()
423
+ print("✓ تم إيقاف Ngrok")
424
+ except:
425
+ pass
426
+
427
+ print('✅ تم الإيقاف بنجاح')