Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import re | |
| import os | |
| import requests | |
| import logging | |
| from typing import Dict, List, Tuple | |
| import json | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class AnonymizerCerebras: | |
| def __init__(self, api_key: str = None): | |
| self.api_key = api_key or os.getenv("CEREBRAS_API_KEY") | |
| self.mapping_table = {} | |
| self.counters = { | |
| 'PERSON': 0, 'COMPANY': 0, 'AMOUNT': 0, 'PHONE': 0, | |
| 'EMAIL': 0, 'ID_NUMBER': 0, 'DATE': 0, 'LOCATION': 0, | |
| 'PERCENTAGE': 0 | |
| } | |
| if not self.api_key: | |
| raise ValueError("❌ کلید API Cerebras یافت نشد!") | |
| logger.info("✅ Anonymizer مقداردهی شد") | |
| def call_cerebras(self, text: str) -> List[Dict]: | |
| """فراخوانی Cerebras API برای شناسایی موجودیتها""" | |
| logger.info("🔄 فراخوانی Cerebras API...") | |
| prompt = f"""شما متخصص شناسایی اطلاعات حساس هستید. | |
| متن زیر را تحلیل کنید و تمام اطلاعات حساسی را شناسایی کنید. | |
| متن: | |
| {text} | |
| یک JSON Array برگردانید. هر عنصر دارای: | |
| - "text": متن اطلاعات حساس | |
| - "type": نوع (PERSON, COMPANY, AMOUNT, PHONE, EMAIL, ID_NUMBER, DATE, LOCATION, PERCENTAGE) | |
| فقط JSON Array برگردانید! | |
| """ | |
| try: | |
| response = requests.post( | |
| "https://api.cerebras.ai/v1/chat/completions", | |
| headers={ | |
| "Authorization": f"Bearer {self.api_key}", | |
| "Content-Type": "application/json" | |
| }, | |
| json={ | |
| "model": "llama-3.3-70b", | |
| "messages": [ | |
| {"role": "system", "content": "شما متخصص تشخیص اطلاعات حساس هستید. فقط JSON برگردانید."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "max_tokens": 3000, | |
| "temperature": 0.1 | |
| }, | |
| timeout=30 | |
| ) | |
| if response.status_code != 200: | |
| logger.error(f"❌ خطای API Cerebras: {response.text}") | |
| return [] | |
| result = response.json() | |
| content = result['choices'][0]['message']['content'] | |
| try: | |
| entities = json.loads(content) | |
| if not isinstance(entities, list): | |
| entities = [] | |
| logger.info(f"✅ {len(entities)} موجودیت استخراج شد") | |
| return entities | |
| except json.JSONDecodeError: | |
| logger.error(f"❌ خطا در JSON parsing") | |
| return [] | |
| except Exception as e: | |
| logger.error(f"❌ خطا Cerebras: {e}") | |
| return [] | |
| def anonymize(self, text: str) -> Tuple[str, List]: | |
| """ناشناسسازی متن با Cerebras""" | |
| logger.info("🚀 شروع ناشناسسازی...") | |
| # تنظیف | |
| self.mapping_table = {} | |
| for key in self.counters: | |
| self.counters[key] = 0 | |
| # دریافت موجودیتها | |
| entities = self.call_cerebras(text) | |
| if not entities: | |
| logger.warning("⚠️ موجودیتی شناسایی نشد") | |
| return text, [] | |
| # جایگزینی | |
| anonymized = text | |
| for entity in entities: | |
| entity_type = entity.get('type', 'UNKNOWN') | |
| entity_text = entity.get('text', '') | |
| if not entity_text or entity_type == 'UNKNOWN': | |
| continue | |
| idx = anonymized.find(entity_text) | |
| if idx == -1: | |
| continue | |
| if entity_type in self.counters: | |
| self.counters[entity_type] += 1 | |
| token = f"[{entity_type}_{self.counters[entity_type]:03d}]" | |
| self.mapping_table[token] = entity_text | |
| anonymized = anonymized[:idx] + token + anonymized[idx + len(entity_text):] | |
| logger.info(f"✅ جایگزین: {entity_text} → {token}") | |
| logger.info(f"✅ ناشناسسازی کامل - {len(self.mapping_table)} نگاشت") | |
| return anonymized, entities | |
| def get_mapping_table_str(self) -> str: | |
| """جدول نگاشت""" | |
| if not self.mapping_table: | |
| return "❌ موجودیتی شناسایی نشد" | |
| result = "## 📊 جدول نگاشت\n\n" | |
| result += "| توکن | اطلاعات اصلی |\n" | |
| result += "|------|-----|\n" | |
| for token, value in sorted(self.mapping_table.items()): | |
| result += f"| `{token}` | {value} |\n" | |
| return result | |
| def restore(self, text: str) -> str: | |
| """بازگردانی اطلاعات اصلی""" | |
| logger.info("🔄 بازگردانی اطلاعات...") | |
| restored = text | |
| for token, value in self.mapping_table.items(): | |
| restored = restored.replace(token, value) | |
| logger.info("✅ بازگردانی کامل") | |
| return restored | |
| # متغیرهای global | |
| anonymizer = None | |
| def process(input_text: str, api_key_cerebras: str, api_key_gpt: str) -> Tuple[str, str, str, str, str, str]: | |
| """ | |
| روند کامل: | |
| 1. ناشناسسازی با Cerebras | |
| 2. ارسال به ChatGPT (حتما!) | |
| 3. بازگردانی پاسخ ChatGPT | |
| """ | |
| global anonymizer | |
| try: | |
| if not input_text.strip(): | |
| return "", "", "", "", "❌ متن خالی است", "" | |
| if not api_key_gpt: | |
| return "", "", "", "", "❌ کلید ChatGPT الزامی است!", "" | |
| # ============================================ | |
| # مرحله 1: مقداردهی | |
| # ============================================ | |
| if not anonymizer: | |
| logger.info("🔧 مقداردهی Anonymizer...") | |
| anonymizer = AnonymizerCerebras(api_key_cerebras if api_key_cerebras else None) | |
| # ============================================ | |
| # مرحله 2: ناشناسسازی | |
| # ============================================ | |
| logger.info("\n" + "="*60) | |
| logger.info("مرحله 1️⃣: ناشناسسازی متن با Cerebras (Llama)") | |
| logger.info("="*60) | |
| anonymized_text, entities = anonymizer.anonymize(input_text) | |
| if not entities: | |
| status = "❌ موجودیتی شناسایی نشد" | |
| return input_text, "", "", "", status, "" | |
| status = f"✅ مرحله 1️⃣: ناشناسسازی کامل\n" | |
| status += f" 📍 {len(entities)} موجودیت شناسایی شد\n\n" | |
| # ============================================ | |
| # مرحله 3: جدول نگاشت | |
| # ============================================ | |
| logger.info("مرحله 2️⃣: ایجاد جدول نگاشت") | |
| mapping = anonymizer.get_mapping_table_str() | |
| status += f"✅ مرحله 2️⃣: جدول نگاشت ایجاد شد\n\n" | |
| # ============================================ | |
| # مرحله 4: ارسال به ChatGPT (حتما!) | |
| # ============================================ | |
| logger.info("="*60) | |
| logger.info("مرحله 3️⃣: ارسال متن ناشناسشده به ChatGPT") | |
| logger.info("="*60) | |
| prompt = f"""متن ناشناسشده زیر را بررسی و تحلیل کنید: | |
| {anonymized_text} | |
| لطفاً خلاصهای مختصر و معنادار ارائه دهید.""" | |
| logger.info(f"📤 ارسال به ChatGPT...\nPrompt:\n{prompt[:200]}...") | |
| try: | |
| gpt_response_obj = requests.post( | |
| "https://api.openai.com/v1/chat/completions", | |
| headers={"Authorization": f"Bearer {api_key_gpt}"}, | |
| json={ | |
| "model": "gpt-4o-mini" , | |
| "messages": [ | |
| {"role": "system", "content": "شما دستیار هوشمند هستید. متنهای ناشناسشده را تحلیل کنید."}, | |
| {"role": "user", "content": prompt} | |
| ], | |
| "max_tokens": 1000, | |
| "temperature": 0.7 | |
| }, | |
| timeout=30 | |
| ) | |
| if gpt_response_obj.status_code == 200: | |
| gpt_response = gpt_response_obj.json()['choices'][0]['message']['content'] | |
| logger.info("✅ پاسخ دریافت شد") | |
| status += f"✅ مرحله 3️⃣: پاسخ ChatGPT دریافت شد\n\n" | |
| else: | |
| error_text = gpt_response_obj.json().get('error', {}).get('message', gpt_response_obj.text) | |
| logger.error(f"❌ خطای ChatGPT: {error_text}") | |
| status += f"❌ مرحله 3️⃣: خطا در ChatGPT\n 📍 {error_text}\n" | |
| return input_text, anonymized_text, "", "", status, mapping | |
| except Exception as e: | |
| logger.error(f"❌ خطا در ارسال: {e}") | |
| status += f"❌ مرحله 3️⃣: خطا - {str(e)}\n" | |
| return input_text, anonymized_text, "", "", status, mapping | |
| # ============================================ | |
| # مرحله 5: بازگردانی پاسخ ChatGPT | |
| # ============================================ | |
| logger.info("="*60) | |
| logger.info("مرحله 4️⃣: بازگردانی پاسخ ChatGPT به حالت اصلی") | |
| logger.info("="*60) | |
| # ⭐⭐⭐ اینجا مهم است! ⭐⭐⭐ | |
| # پاسخ ChatGPT را با جدول نگاشت بازگردانی کن | |
| # نه متن ناشناس شده را! | |
| restored_text = anonymizer.restore(gpt_response) | |
| logger.info(f"✅ بازگردانی کامل") | |
| status += f"✅ مرحله 4️⃣: بازگردانی پاسخ ChatGPT به حالت اصلی\n\n" | |
| status += "🎉 پردازش کامل شد!" | |
| logger.info("="*60) | |
| logger.info("📊 خلاصه:") | |
| logger.info(f" • ورودی: {len(input_text)} کاراکتر") | |
| logger.info(f" • ناشناس: {len(anonymized_text)} کاراکتر") | |
| logger.info(f" • موجودیتها: {len(entities)}") | |
| logger.info(f" • نگاشتها: {len(anonymizer.mapping_table)}") | |
| logger.info(f" • پاسخ ChatGPT: {len(gpt_response)} کاراکتر") | |
| logger.info(f" • نتیجه نهایی: {len(restored_text)} کاراکتر") | |
| logger.info("="*60) | |
| return input_text, anonymized_text, gpt_response, restored_text, status, mapping | |
| except Exception as e: | |
| logger.error(f"❌ خطا: {e}", exc_info=True) | |
| return "", "", "", "", f"❌ خطا: {str(e)}", "" | |
| def clear(): | |
| """پاک کردن""" | |
| return "", "", "", "", "", "" | |
| # رابط Gradio | |
| with gr.Blocks(title="Anonymization + ChatGPT", theme=gr.themes.Soft()) as app: | |
| # هدر | |
| gr.Markdown(""" | |
| # 📊 سیستم ناشناسسازی + تحلیل ChatGPT | |
| ### روند کار: | |
| 1. **متن ورودی** → Cerebras (Llama 3.3-70B) → **ناشناسسازی** | |
| 2. **متن ناشناس** → ChatGPT → **تحلیل و پاسخ** | |
| 3. **پاسخ ChatGPT** → جدول نگاشت → **بازگردانی به حالت اصلی** | |
| """) | |
| # ردیف 1: تنظیمات | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### ⚙️ تنظیمات API") | |
| api_key_cerebras = gr.Textbox( | |
| label="🔑 Cerebras API Key *", | |
| placeholder="برای ناشناسسازی", | |
| type="password", | |
| info="الزامی" | |
| ) | |
| api_key_gpt = gr.Textbox( | |
| label="🔑 OpenAI API Key *", | |
| placeholder="برای تحلیل متن", | |
| type="password", | |
| info="الزامی" | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 🎮 کنترلها") | |
| gr.Markdown("") | |
| process_btn = gr.Button("🚀 شروع پردازش", variant="primary", size="lg") | |
| clear_btn = gr.Button("🗑️ پاک کردن", variant="stop") | |
| gr.Markdown("") | |
| gr.Markdown("**هر دو API الزامی است**") | |
| # ردیف 2: 4 باکس در وسط | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### 📝 1️⃣ متن ورودی") | |
| input_text = gr.Textbox( | |
| lines=10, | |
| placeholder="متن خود را وارد کنید...\nمثال:\nشرکت بانک ملی از طریق مدیرعامل احمد علی\nدرآمد 600 میلیارد تومان را اعلام کرد", | |
| label="" | |
| ) | |
| with gr.Column(): | |
| gr.Markdown("### 🎭 2️⃣ متن ناشناسشده") | |
| gr.Markdown("*(برای ارسال به ChatGPT)*") | |
| anonymized_text = gr.Textbox( | |
| lines=10, | |
| label="", | |
| interactive=False | |
| ) | |
| with gr.Column(): | |
| gr.Markdown("### 🤖 3️⃣ پاسخ ChatGPT") | |
| gr.Markdown("*(قبل از بازگردانی)*") | |
| gpt_response = gr.Textbox( | |
| lines=10, | |
| label="", | |
| interactive=False | |
| ) | |
| with gr.Column(): | |
| gr.Markdown("### ✅ 4️⃣ نتیجه نهایی") | |
| gr.Markdown("*(پاسخ بازگردانیشده)*") | |
| restored_text = gr.Textbox( | |
| lines=10, | |
| label="", | |
| interactive=False | |
| ) | |
| # ردیف 3: وضعیت و جدول نگاشت | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### ⚙️ وضعیت پردازش") | |
| status = gr.Textbox( | |
| lines=8, | |
| label="", | |
| interactive=False | |
| ) | |
| with gr.Column(): | |
| gr.Markdown("### 📋 جدول نگاشت") | |
| gr.Markdown("*(برای بازگردانی)*") | |
| mapping = gr.Textbox( | |
| lines=8, | |
| label="", | |
| interactive=False | |
| ) | |
| # Event handlers | |
| process_btn.click( | |
| fn=process, | |
| inputs=[input_text, api_key_cerebras, api_key_gpt], | |
| outputs=[input_text, anonymized_text, gpt_response, restored_text, status, mapping] | |
| ) | |
| clear_btn.click( | |
| fn=clear, | |
| outputs=[input_text, anonymized_text, gpt_response, restored_text, status, mapping] | |
| ) | |
| if __name__ == "__main__": | |
| print(""" | |
| ╔═══════════════════════════════════════════════════════════╗ | |
| ║ 📊 ناشناسسازی + تحلیل ChatGPT ║ | |
| ║ Cerebras Llama 3.3-70B + OpenAI ║ | |
| ╚═══════════════════════════════════════════════════════════╝ | |
| 🚀 شروع: | |
| pip install gradio requests | |
| python app_correct.py | |
| 📝 روند: | |
| متن ورودی | |
| ↓ | |
| 1️⃣ Cerebras: ناشناسسازی | |
| ↓ | |
| 2️⃣ جدول نگاشت | |
| ↓ | |
| 3️⃣ ChatGPT: تحلیل (حتما!) | |
| ↓ | |
| 4️⃣ بازگردانی: پاسخ ChatGPT | |
| 💡 کلید نکته: | |
| - ناشناس → ChatGPT | |
| - ChatGPT پاسخ → بازگردانی | |
| - نه ناشناس را برنمیگردونی! | |
| """) | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| show_error=True | |
| ) | |