Spaces:
Sleeping
Sleeping
| import os | |
| import time | |
| import json | |
| import google.generativeai as genai | |
| from docx import Document | |
| import gradio as gr | |
| # --- قائمة النماذج --- | |
| USER_MODELS = [ | |
| "models/gemini-2.5-flash", | |
| "models/gemini-2.5-pro", | |
| "models/gemini-2.0-flash-exp", | |
| "models/gemini-2.0-flash", | |
| "models/gemini-2.0-flash-001", | |
| "models/gemini-2.0-flash-lite-preview-02-05", | |
| "models/gemini-1.5-flash", | |
| "models/gemini-1.5-pro", | |
| ] | |
| # --- كلاس إدارة المفاتيح والإحصائيات --- | |
| class KeyManager: | |
| def __init__(self, keys_list): | |
| # تخزين المفاتيح مع عداد لكل مفتاح | |
| self.keys_data = [{"key": k, "count": 0, "status": "active"} for k in keys_list] | |
| self.current_index = 0 | |
| self.total_requests = 0 | |
| def get_next_key(self): | |
| """جلب المفتاح التالي بنظام التدوير (Round Robin)""" | |
| if not self.keys_data: return None | |
| key_data = self.keys_data[self.current_index] | |
| current_key = key_data["key"] | |
| # تحديث المؤشر للمرة القادمة | |
| self.current_index = (self.current_index + 1) % len(self.keys_data) | |
| return current_key, self.keys_data.index(key_data) | |
| def increment_usage(self, index): | |
| """زيادة العداد للمفتاح المستخدم""" | |
| self.keys_data[index]["count"] += 1 | |
| self.total_requests += 1 | |
| def get_stats_html(self): | |
| """توليد جدول HTML للإحصائيات""" | |
| rows = "" | |
| max_usage = max([k["count"] for k in self.keys_data]) if self.keys_data else 1 | |
| for k in self.keys_data: | |
| # إخفاء جزء من المفتاح للأمان | |
| masked_key = k['key'][:6] + "..." + k['key'][-4:] if len(k['key']) > 10 else "KEY" | |
| count = k['count'] | |
| # شريط تقدم بسيط | |
| width_percent = (count / max_usage * 100) if max_usage > 0 else 0 | |
| rows += f""" | |
| <tr style="border-bottom: 1px solid #eee;"> | |
| <td style="padding: 5px; font-family: monospace;">{masked_key}</td> | |
| <td style="padding: 5px; text-align: center; font-weight: bold; color: #2ecc71;">{count}</td> | |
| <td style="padding: 5px; width: 50%;"> | |
| <div style="background: #e0e0e0; height: 8px; border-radius: 4px; overflow: hidden;"> | |
| <div style="background: #3498db; width: {width_percent}%; height: 100%;"></div> | |
| </div> | |
| </td> | |
| </tr> | |
| """ | |
| html = f""" | |
| <div style="direction: rtl; background: #f9f9f9; padding: 10px; border-radius: 8px; border: 1px solid #ddd;"> | |
| <h4 style="margin-top: 0; color: #333;">📊 إحصائيات استهلاك المفاتيح</h4> | |
| <p><b>إجمالي الطلبات الناجحة:</b> {self.total_requests}</p> | |
| <table style="width: 100%; font-size: 13px; border-collapse: collapse;"> | |
| <thead> | |
| <tr style="background: #eee;"> | |
| <th style="padding: 5px; text-align: right;">المفتاح</th> | |
| <th style="padding: 5px; text-align: center;">الطلبات</th> | |
| <th style="padding: 5px; text-align: center;">مؤشر الحمل</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {rows} | |
| </tbody> | |
| </table> | |
| </div> | |
| """ | |
| return html | |
| # --- دوال مساعدة --- | |
| def read_docx_and_chunk(file_obj, chunk_size=4000): | |
| try: | |
| if not file_obj: return [] | |
| doc = Document(file_obj.name) | |
| full_text = "\n".join([p.text for p in doc.paragraphs if p.text.strip()]) | |
| chunks = [] | |
| current_chunk = "" | |
| for paragraph in full_text.split('\n'): | |
| if len(current_chunk) + len(paragraph) < chunk_size: | |
| current_chunk += paragraph + "\n" | |
| else: | |
| chunks.append(current_chunk) | |
| current_chunk = paragraph + "\n" | |
| if current_chunk: chunks.append(current_chunk) | |
| return chunks | |
| except: return [] | |
| def clean_json_text(text): | |
| text = text.strip() | |
| if text.startswith('```json'): text = text[7:] | |
| if text.startswith('```'): text = text[3:] | |
| if text.endswith('```'): text = text[:-3] | |
| return text.strip() | |
| def parse_api_keys(api_text, api_file): | |
| keys = [] | |
| if api_file is not None: | |
| try: | |
| with open(api_file.name, 'r', encoding='utf-8') as f: | |
| content = f.read() | |
| keys = [line.split(',')[0].strip() for line in content.split('\n') if line.strip()] | |
| except: pass | |
| if not keys and api_text.strip(): | |
| keys = [k.strip() for k in api_text.split('\n') if k.strip()] | |
| return keys | |
| def generate_fiqh_system_instruction(author, madhab, field, book_type): | |
| base_prompt = f""" | |
| أنت باحث شرعي وخبير متخصص في تحويل التراث الإسلامي إلى بيانات هيكلية. | |
| النص من مذهب: ({madhab})، فن: ({field}). المؤلف: ({author}). النوع: ({book_type}). | |
| """ | |
| if "متن" in book_type: | |
| specific = 'استخرج القواعد والمسائل (المنطوق) بدقة.' | |
| else: | |
| specific = 'اربط الشرح بالمتن واستخرج المسألة مع التعليل والدليل.' | |
| output_format = """ | |
| المطلوب: JSON List فقط. | |
| كل عنصر: {"instruction": "السؤال/المسألة", "input": "السياق (اختياري)", "output": "الجواب المحرر", "source_meta": "المصدر"} | |
| """ | |
| return base_prompt + specific + output_format | |
| # --- المحرك الرئيسي --- | |
| def process_fiqh_book(file_obj, api_text, api_file, selected_model, author, madhab, field, book_type, delay): | |
| # 1. إعداد المفاتيح | |
| keys_list = parse_api_keys(api_text, api_file) | |
| if not keys_list: return None, None, "⚠️ يجب إدخال مفاتيح API." | |
| # إنشاء مدير المفاتيح | |
| key_manager = KeyManager(keys_list) | |
| if not file_obj: return None, key_manager.get_stats_html(), "⚠️ المرجو رفع ملف الكتاب." | |
| chunks = read_docx_and_chunk(file_obj) | |
| if not chunks: return None, key_manager.get_stats_html(), "⚠️ الملف فارغ أو معطوب." | |
| system_instruction = generate_fiqh_system_instruction(author, madhab, field, book_type) | |
| log_msg = f"🚀 تم تحميل {len(keys_list)} مفتاح.\nبدء المعالجة (توزيع الحمل المتساوي)...\n" | |
| yield None, key_manager.get_stats_html(), log_msg | |
| processed_data = [] | |
| for i, chunk in enumerate(chunks): | |
| # الحصول على المفتاح التالي في الدور | |
| current_key, key_index = key_manager.get_next_key() | |
| try: | |
| genai.configure(api_key=current_key) | |
| model = genai.GenerativeModel( | |
| model_name=selected_model, | |
| system_instruction=system_instruction, | |
| generation_config={"response_mime_type": "application/json"} | |
| ) | |
| response = model.generate_content(chunk) | |
| if not response.text: raise ValueError("رد فارغ") | |
| data = json.loads(clean_json_text(response.text)) | |
| for item in data: | |
| item['author'] = author | |
| item['madhab'] = madhab | |
| processed_data.extend(data) | |
| # تسجيل النجاح وتحديث الإحصائيات | |
| key_manager.increment_usage(key_index) | |
| log_msg += f"✅ جزء {i+1} تم ({len(data)} مسألة) -> مفتاح {current_key[:5]}...\n" | |
| # تحديث الواجهة (الجدول واللوج) | |
| yield None, key_manager.get_stats_html(), log_msg | |
| time.sleep(delay) | |
| except Exception as e: | |
| log_msg += f"❌ خطأ في جزء {i+1}: {str(e)}\n" | |
| yield None, key_manager.get_stats_html(), log_msg | |
| if processed_data: | |
| fname = "fiqh_data.json" | |
| with open(fname, 'w', encoding='utf-8') as f: | |
| json.dump(processed_data, f, ensure_ascii=False, indent=4) | |
| yield fname, key_manager.get_stats_html(), log_msg + "\n🎉 اكتملت المهمة." | |
| else: | |
| yield None, key_manager.get_stats_html(), log_msg + "\n⚠️ لم يتم استخراج بيانات." | |
| # --- الواجهة --- | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# 📚 محول الفقه (نظام توزيع الحمل المتوازن)") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 1️⃣ المفاتيح والإعدادات") | |
| api_file = gr.File(label="ملف المفاتيح (txt)") | |
| api_text = gr.Textbox(label="أو لصق المفاتيح", lines=3, placeholder="KEY_1\nKEY_2", type="password") | |
| model_in = gr.Dropdown(USER_MODELS, value="models/gemini-2.5-flash", label="الموديل") | |
| with gr.Group(): | |
| author_in = gr.Textbox(label="المؤلف") | |
| madhab_in = gr.Dropdown(['المالكي', 'الشافعي', 'الحنفي', 'الحنبلي'], value='المالكي', label="المذهب") | |
| field_in = gr.Textbox(value="الفقه", label="الفن") | |
| type_in = gr.Dropdown(['متن', 'شرح'], value='شرح', label="النوع") | |
| delay_in = gr.Slider(0, 10, 1, label="تأخير (ث)") | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 2️⃣ التشغيل والمراقبة") | |
| file_in = gr.File(label="ملف الكتاب (DOCX)") | |
| btn = gr.Button("🚀 ابدأ المعالجة", variant="primary") | |
| # مكون عرض الإحصائيات الجديد | |
| stats_view = gr.HTML(label="إحصائيات المفاتيح") | |
| logs = gr.Textbox(label="سجل العمليات", lines=8) | |
| f_out = gr.File(label="تحميل النتيجة") | |
| btn.click( | |
| fn=process_fiqh_book, | |
| inputs=[file_in, api_text, api_file, model_in, author_in, madhab_in, field_in, type_in, delay_in], | |
| outputs=[f_out, stats_view, logs] | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue().launch() |