FECUOY commited on
Commit
53ea9be
·
verified ·
1 Parent(s): c9efaf4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -69
app.py CHANGED
@@ -2,96 +2,167 @@ import gradio as gr
2
  from huggingface_hub import InferenceClient
3
  import zipfile
4
  import os
5
- import fitz # PyMuPDF
6
- import docx
7
 
8
- def process_files(files):
9
- """معالجة كافة أنواع الملفات المستلمة وتحويلها لنصوص"""
10
- combined_text = ""
11
- for file in files:
12
- file_name = os.path.basename(file.name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # 1. التعامل مع ملفات ZIP
15
- if file_name.endswith('.zip'):
16
- combined_text += f"\n--- محتوى ملف مضغوط: {file_name} ---\n"
17
- with zipfile.ZipFile(file.name, 'r') as z:
18
- file_list = z.namelist()
19
- combined_text += f"قائمة الملفات الموجودة ({len(file_list)} ملف):\n"
20
- combined_text += "\n".join(file_list[:50]) # عرض أول 50 ملف كفهرس
21
- if len(file_list) > 50: combined_text += "\n...وغيرها الكثير."
 
 
 
 
 
22
 
23
- # قراءة محتوى أول ملفات نصية كعينة
24
- for f_inner in file_list[:5]:
25
- if f_inner.endswith(('.txt', '.py', '.md', '.json')):
26
- with z.open(f_inner) as f:
 
 
 
 
 
27
  content = f.read().decode('utf-8', errors='ignore')
28
- combined_text += f"\nمحتوى عينة {f_inner}:\n{content[:500]}...\n"
 
 
29
 
30
- # 2. التعامل مع PDF
31
- elif file_name.endswith('.pdf'):
32
- with fitz.open(file.name) as doc:
33
- text = "".join([page.get_text() for page in doc])
34
- combined_text += f"\n--- محتوى PDF: {file_name} ---\n{text[:20000]}\n"
35
 
36
- # 3. التعامل مع Word
37
- elif file_name.endswith('.docx'):
38
- doc = docx.Document(file.name)
39
- text = "\n".join([p.text for p in doc.paragraphs])
40
- combined_text += f"\n--- محتوى Word: {file_name} ---\n{text[:20000]}\n"
41
-
42
- return combined_text
43
 
44
- def respond(message, history, system_message, max_tokens, temperature, top_p, hf_token: gr.OAuthToken):
45
- if not hf_token:
46
- yield "⚠️ يرجى تسجيل الدخول باستخدام زر Hugging Face في القائمة الجانبية."
 
 
 
 
 
 
 
 
 
47
  return
48
 
49
- client = InferenceClient(model="moonshotai/Kimi-K2-Instruct", token=hf_token.token)
50
-
51
- # استخراج النصوص من الرسالة المتعددة الوسائط
 
 
 
 
52
  user_text = message.get("text", "")
53
  files = message.get("files", [])
54
 
55
- file_context = ""
 
56
  if files:
57
- yield "⏳ جاري تحليل الملفات..."
58
- file_context = process_files(files)
59
-
60
- # بناء سياق المحادثة
61
- full_user_message = f"{file_context}\n\nالسؤال: {user_text}" if file_context else user_text
 
 
 
 
 
 
 
62
 
 
63
  messages = [{"role": "system", "content": system_message}]
64
  messages.extend(history)
65
- messages.append({"role": "user", "content": full_user_message})
66
 
67
- response = ""
 
68
  try:
69
- for msg in client.chat_completion(messages, max_tokens=max_tokens, stream=True, temperature=temperature, top_p=top_p):
70
- token = msg.choices[0].delta.content
71
- if token:
72
- response += token
73
- yield response
 
 
 
 
 
 
 
74
  except Exception as e:
75
- yield f"⚠️ حدث خطأ أثناء الاتصال بالنموذج: {str(e)}"
76
 
77
- # واجهة Gradio الاحترافية
78
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
79
- gr.Markdown("# 🤖 Kimi-K2 Advanced Chatbot (File Analyzer)")
 
 
 
 
 
80
 
81
- with gr.Sidebar():
82
- gr.LoginButton()
83
- gr.Markdown("---")
84
- system_input = gr.Textbox(value="أنت مساعد خبير في تحليل البيانات والملفات.", label="تعليمات النظام")
85
- max_tokens = gr.Slider(100, 8192, 2048, label="Max Tokens")
86
- temp = gr.Slider(0.1, 1.5, 0.7, label="Temperature")
 
 
 
 
 
 
 
 
 
87
 
88
- chat = gr.ChatInterface(
89
- respond,
90
- additional_inputs=[system_input, max_tokens, temp, gr.State(0.95)],
91
- multimodal=True,
92
- type="messages",
93
- examples=[{"text": "لخص محتويات هذا الملف", "files": []}]
94
- )
 
 
 
 
95
 
96
  if __name__ == "__main__":
97
- demo.launch()
 
 
2
  from huggingface_hub import InferenceClient
3
  import zipfile
4
  import os
5
+ import fitz # مكتبة PyMuPDF لقراءة ملفات PDF
6
+ import docx # مكتبة لقراءة ملفات Word
7
 
8
+ # --- إعدادات المعالجة ---
9
+ MAX_CHAR_LIMIT = 50000 # أقصى عدد حروف يتم قراءته من الملفات لتجنب تجاوز ذاكرة النموذج
10
+ MAX_ZIP_FILES_PREVIEW = 20 # عدد الملفات التي يتم قراءتها من داخل المجلد المضغوط
11
+
12
+ def extract_text_from_file(file_path):
13
+ """دالة شاملة لاستخراج النصوص بناءً على نوع الملف"""
14
+ try:
15
+ filename = os.path.basename(file_path)
16
+ ext = filename.split('.')[-1].lower()
17
+
18
+ # 1. معالجة PDF
19
+ if ext == 'pdf':
20
+ text = ""
21
+ with fitz.open(file_path) as doc:
22
+ for page in doc:
23
+ text += page.get_text()
24
+ return f"\n--- بداية ملف PDF: {filename} ---\n{text[:MAX_CHAR_LIMIT]}\n--- نهاية الملف ---\n"
25
+
26
+ # 2. معالجة Word (DOCX)
27
+ elif ext == 'docx':
28
+ doc = docx.Document(file_path)
29
+ text = "\n".join([para.text for para in doc.paragraphs])
30
+ return f"\n--- بداية ملف Word: {filename} ---\n{text[:MAX_CHAR_LIMIT]}\n--- نهاية الملف ---\n"
31
 
32
+ # 3. معالجة الملفات النصية والبرمجية (txt, py, md, json, html, csv)
33
+ elif ext in ['txt', 'py', 'js', 'html', 'css', 'md', 'json', 'csv', 'xml']:
34
+ with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
35
+ content = f.read()
36
+ return f"\n--- بداية ملف نصي: {filename} ---\n{content[:MAX_CHAR_LIMIT]}\n--- نهاية الملف ---\n"
37
+
38
+ # 4. معالجة ملفات ZIP (الذكية)
39
+ elif ext == 'zip':
40
+ report = f"\n--- تحليل مجلد مضغوط: {filename} ---\n"
41
+ with zipfile.ZipFile(file_path, 'r') as z:
42
+ all_files = z.namelist()
43
+ report += f"المجلد يحتوي على {len(all_files)} ملف/مجلد.\n"
44
+ report += "قائمة الملفات (أول 50):\n" + "\n".join(all_files[:50]) + "\n"
45
 
46
+ # قراءة محتوى عينة من الملفات داخل الـ ZIP
47
+ read_count = 0
48
+ for sub_file in all_files:
49
+ if read_count >= MAX_ZIP_FILES_PREVIEW: break
50
+ if sub_file.endswith(('/', '\\')): continue # تجاهل المجلدات الفارغة
51
+
52
+ # محاولة قراءة الملفات النصية داخل الـ ZIP
53
+ if sub_file.lower().endswith(('.txt', '.py', '.md', '.json', '.csv')):
54
+ with z.open(sub_file) as f:
55
  content = f.read().decode('utf-8', errors='ignore')
56
+ report += f"\n[محتوى ملف داخل ZIP: {sub_file}]\n{content[:2000]}...\n"
57
+ read_count += 1
58
+ return report + "\n--- نهاية المجلد المضغوط ---\n"
59
 
60
+ else:
61
+ return f"\n⚠️ تنبيه: الملف {filename} بصيغة غير مدعومة للقراءة النصية المباشرة.\n"
 
 
 
62
 
63
+ except Exception as e:
64
+ return f"\n⚠️ خطأ أثناء قراءة الملف {os.path.basename(file_path)}: {str(e)}\n"
 
 
 
 
 
65
 
66
+ def respond(
67
+ message,
68
+ history: list[dict[str, str]],
69
+ system_message,
70
+ max_tokens,
71
+ temperature,
72
+ top_p,
73
+ hf_token: gr.OAuthToken,
74
+ ):
75
+ # 1. التحقق من التوكن
76
+ if not hf_token or not hf_token.token:
77
+ yield "⚠️ **خطأ:** يرجى تسجيل الدخول بحساب Hugging Face أولاً من الزر الموجود في الأعلى."
78
  return
79
 
80
+ # إعداد العميل (Client)
81
+ client = InferenceClient(
82
+ model="moonshotai/Kimi-K2-Instruct",
83
+ token=hf_token.token
84
+ )
85
+
86
+ # 2. استخراج البيانات من رسالة المستخدم
87
  user_text = message.get("text", "")
88
  files = message.get("files", [])
89
 
90
+ # 3. معالجة الملفات (إن وجدت)
91
+ extracted_knowledge = ""
92
  if files:
93
+ yield "⏳ **جاري تحليل الملفات وقراءة محتوياتها...**"
94
+ for file_obj in files:
95
+ # Gradio يعيد مسار الملف في file_obj أو file_obj['path']
96
+ path = file_obj if isinstance(file_obj, str) else file_obj.name
97
+ extracted_knowledge += extract_text_from_file(path)
98
+
99
+ # 4. بناء الرسالة النهائية للنموذج
100
+ full_prompt = ""
101
+ if extracted_knowledge:
102
+ full_prompt = f"البيانات المستخرجة من الملفات المرفقة:\n{extracted_knowledge}\n\n"
103
+
104
+ full_prompt += f"سؤال/طلب المستخدم: {user_text}"
105
 
106
+ # 5. تجهيز سجل المحادثة
107
  messages = [{"role": "system", "content": system_message}]
108
  messages.extend(history)
109
+ messages.append({"role": "user", "content": full_prompt})
110
 
111
+ # 6. البث المباشر للإجابة (Streaming)
112
+ response_text = ""
113
  try:
114
+ for msg in client.chat_completion(
115
+ messages,
116
+ max_tokens=max_tokens,
117
+ stream=True,
118
+ temperature=temperature,
119
+ top_p=top_p,
120
+ ):
121
+ if msg.choices:
122
+ token = msg.choices[0].delta.content
123
+ if token:
124
+ response_text += token
125
+ yield response_text
126
  except Exception as e:
127
+ yield f"⚠️ **حدث خطأ من المصدر:** {str(e)}\n\nتأكد من أن الملفات ليست ضخمة جداً (أكبر من سياق النموذج) أو حاول تقليل عدد الملفات."
128
 
129
+ # --- واجهة المستخدم (UI) ---
130
+ with gr.Blocks(theme=gr.themes.Soft(), title="Advanced AI Assistant") as demo:
131
+ gr.Markdown(
132
+ """
133
+ # 🤖 المساعد الذكي المتكامل (تحليل ملفات + دردشة)
134
+ قم برفع ملفات PDF, Word, أو ZIP (حتى لو كانت كبيرة) واسأل عنها مباشرة.
135
+ """
136
+ )
137
 
138
+ with gr.Row():
139
+ with gr.Column(scale=1):
140
+ gr.LoginButton(value="تسجيل دخول Hugging Face")
141
+
142
+ with gr.Accordion("⚙️ إعدادات النموذج", open=False):
143
+ system_msg = gr.Textbox(
144
+ value="أنت مساعد ذكي ومحلل بيانات خبير. قم بالإجابة باللغة العربية بوضوح.",
145
+ label="تعليمات النظام (System Prompt)",
146
+ lines=3
147
+ )
148
+ max_tokens = gr.Slider(512, 8192, value=2048, step=64, label="طول الإجابة (Max Tokens)")
149
+ temperature = gr.Slider(0.1, 2.0, value=0.6, step=0.1, label="الإبداع (Temperature)")
150
+
151
+ # ... تكملة السطر الأخير الذي توقف عندك
152
+ top_p = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-p")
153
 
154
+ with gr.Column(scale=4):
155
+ # إعداد واجهة الدردشة
156
+ gr.ChatInterface(
157
+ respond,
158
+ chatbot=gr.Chatbot(height=600, type="messages"), # تحديد الطول والنوع
159
+ type="messages",
160
+ multimodal=True, # ضروري لظهور زر رفع الملفات
161
+ additional_inputs=[system_msg, max_tokens, temperature, top_p],
162
+ title="Kimi-K2 File Analysis Bot",
163
+ description="ارفع ملفاتك (ZIP, PDF, DOCX) وسأقوم بتحليلها والإجابة عن أسئلتك.",
164
+ )
165
 
166
  if __name__ == "__main__":
167
+ demo.queue().launch()
168
+