|
|
|
|
|
|
|
|
|
|
|
|
|
|
import requests |
|
|
import json |
|
|
import gradio as gr |
|
|
from typing import Dict, Any |
|
|
import os |
|
|
from dataclasses import dataclass |
|
|
|
|
|
@dataclass |
|
|
class GroqConfig: |
|
|
"""تنظیمات Groq API""" |
|
|
api_key: str |
|
|
base_url: str = "https://api.groq.com/openai/v1" |
|
|
model: str = "llama-3.1-8b-instant" |
|
|
max_tokens: int = 1000 |
|
|
temperature: float = 0.1 |
|
|
|
|
|
class GroqAnonymizer: |
|
|
"""سیستم ناشناسسازی با استفاده از Groq""" |
|
|
|
|
|
def __init__(self, api_key: str = None): |
|
|
|
|
|
if api_key is None: |
|
|
api_key = os.getenv("GROQ_API_KEY") |
|
|
if not api_key: |
|
|
raise ValueError("کلید API در متغیرهای محیطی یافت نشد. لطفاً GROQ_API_KEY را تنظیم کنید.") |
|
|
|
|
|
self.config = GroqConfig(api_key=api_key) |
|
|
self.system_prompt = self._create_system_prompt() |
|
|
|
|
|
def _create_system_prompt(self) -> str: |
|
|
"""ایجاد دستورالعمل سیستمی برای Groq""" |
|
|
return """شما یک سیستم ناشناسسازی متن فارسی هستید. |
|
|
|
|
|
وظیفه: تشخیص و جایگزینی موجودیتهای حساس: |
|
|
|
|
|
1. نام شرکتها → company-01, company-02, ... |
|
|
2. نام افراد → person-01, person-02, ... |
|
|
3. مبالغ و اعداد → amount-01, amount-02, ... |
|
|
4. درصدها → percent-01, percent-02, ... |
|
|
|
|
|
نکات: |
|
|
- همان موجودیت = همان شماره |
|
|
- پیشوندها (دکتر، آقا) را حفظ کنید |
|
|
- فقط متن ناشناسسازی شده را برگردانید، بدون JSON یا توضیح اضافی |
|
|
|
|
|
مثال: |
|
|
ورودی: احمد رضایی مدیرعامل شرکت پارس 100 میلیون تومان درآمد دارد |
|
|
خروجی: person-01 مدیرعامل company-01 amount-01 درآمد دارد""" |
|
|
|
|
|
def _make_api_request(self, text: str) -> Dict[str, Any]: |
|
|
"""ارسال درخواست به Groq API""" |
|
|
headers = { |
|
|
"Authorization": f"Bearer {self.config.api_key}", |
|
|
"Content-Type": "application/json" |
|
|
} |
|
|
|
|
|
payload = { |
|
|
"messages": [ |
|
|
{ |
|
|
"role": "system", |
|
|
"content": self.system_prompt |
|
|
}, |
|
|
{ |
|
|
"role": "user", |
|
|
"content": text |
|
|
} |
|
|
], |
|
|
"model": self.config.model, |
|
|
"temperature": self.config.temperature, |
|
|
"max_tokens": self.config.max_tokens |
|
|
} |
|
|
|
|
|
try: |
|
|
print(f"DEBUG: Sending request to {self.config.base_url}/chat/completions") |
|
|
print(f"DEBUG: Model: {self.config.model}") |
|
|
print(f"DEBUG: Text length: {len(text)}") |
|
|
|
|
|
response = requests.post( |
|
|
f"{self.config.base_url}/chat/completions", |
|
|
headers=headers, |
|
|
json=payload, |
|
|
timeout=30 |
|
|
) |
|
|
|
|
|
print(f"DEBUG: Response status: {response.status_code}") |
|
|
|
|
|
if response.status_code != 200: |
|
|
print(f"DEBUG: Error response: {response.text}") |
|
|
|
|
|
response.raise_for_status() |
|
|
return response.json() |
|
|
|
|
|
except requests.exceptions.RequestException as e: |
|
|
print(f"DEBUG: Request exception: {str(e)}") |
|
|
raise Exception(f"خطا در ارتباط با Groq API: {str(e)}") |
|
|
|
|
|
def anonymize_text(self, text: str) -> Dict[str, Any]: |
|
|
"""ناشناسسازی متن با استفاده از Groq""" |
|
|
if not text.strip(): |
|
|
return { |
|
|
"success": False, |
|
|
"error": "متن ورودی خالی است" |
|
|
} |
|
|
|
|
|
try: |
|
|
response = self._make_api_request(text) |
|
|
|
|
|
if "choices" not in response or not response["choices"]: |
|
|
return { |
|
|
"success": False, |
|
|
"error": "پاسخ نامعتبر از API" |
|
|
} |
|
|
|
|
|
content = response["choices"][0]["message"]["content"] |
|
|
|
|
|
|
|
|
if "```" in content: |
|
|
lines = content.split('\n') |
|
|
clean_lines = [] |
|
|
skip = False |
|
|
for line in lines: |
|
|
if line.strip().startswith('```'): |
|
|
skip = not skip |
|
|
continue |
|
|
if not skip: |
|
|
clean_lines.append(line) |
|
|
content = '\n'.join(clean_lines) |
|
|
|
|
|
|
|
|
content = content.strip() |
|
|
|
|
|
return { |
|
|
"success": True, |
|
|
"anonymized_text": content, |
|
|
"entities": [], |
|
|
"statistics": self._count_entities(content), |
|
|
"usage": response.get("usage", {}) |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
return { |
|
|
"success": False, |
|
|
"error": f"خطا در پردازش: {str(e)}" |
|
|
} |
|
|
|
|
|
def _count_entities(self, text: str) -> Dict[str, int]: |
|
|
"""شمارش موجودیتها در متن ناشناسسازی شده""" |
|
|
import re |
|
|
|
|
|
company_count = len(re.findall(r'company-\d+', text)) |
|
|
person_count = len(re.findall(r'person-\d+', text)) |
|
|
amount_count = len(re.findall(r'amount-\d+', text)) |
|
|
percent_count = len(re.findall(r'percent-\d+', text)) |
|
|
group_count = len(re.findall(r'group-\d+', text)) |
|
|
|
|
|
return { |
|
|
"company": company_count, |
|
|
"person": person_count, |
|
|
"amount": amount_count, |
|
|
"percent": percent_count, |
|
|
"group": group_count |
|
|
} |
|
|
|
|
|
def create_interface(): |
|
|
"""ایجاد رابط کاربری برای Hugging Face Spaces""" |
|
|
|
|
|
|
|
|
api_key_available = bool(os.getenv("GROQ_API_KEY")) |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
.gradio-container { |
|
|
font-family: 'Tahoma', 'Arial', sans-serif !important; |
|
|
direction: rtl; |
|
|
max-width: 1200px; |
|
|
margin: 0 auto; |
|
|
} |
|
|
.result-box { |
|
|
background-color: #f0f8ff; |
|
|
border: 1px solid #ddd; |
|
|
border-radius: 8px; |
|
|
padding: 15px; |
|
|
font-family: 'Tahoma', monospace; |
|
|
line-height: 1.6; |
|
|
} |
|
|
.warning-box { |
|
|
background-color: #fff3cd; |
|
|
border: 1px solid #ffeaa7; |
|
|
border-radius: 8px; |
|
|
padding: 10px; |
|
|
color: #856404; |
|
|
} |
|
|
.success-box { |
|
|
background-color: #d4edda; |
|
|
border: 1px solid #c3e6cb; |
|
|
border-radius: 8px; |
|
|
padding: 10px; |
|
|
color: #155724; |
|
|
} |
|
|
""" |
|
|
|
|
|
with gr.Blocks(css=custom_css, title="ناشناسسازی متن فارسی", theme=gr.themes.Soft()) as interface: |
|
|
|
|
|
|
|
|
gr.Markdown(""" |
|
|
# 🔒 سیستم ناشناسسازی متن فارسی |
|
|
### قدرتگرفته از Groq AI برای تشخیص و حفاظت از اطلاعات حساس |
|
|
|
|
|
این سیستم قادر است نام افراد، شرکتها، مبالغ مالی و درصدها را به صورت هوشمند تشخیص و ناشناسسازی کند. |
|
|
""") |
|
|
|
|
|
|
|
|
if not api_key_available: |
|
|
gr.Markdown(""" |
|
|
<div class="warning-box"> |
|
|
⚠️ <strong>کلید API در متغیرهای محیطی یافت نشد</strong><br> |
|
|
لطفاً کلید Groq API خود را در زیر وارد کنید یا در Settings > Secrets تنظیم کنید |
|
|
</div> |
|
|
""") |
|
|
|
|
|
api_key_input = gr.Textbox( |
|
|
label="🔑 کلید Groq API", |
|
|
placeholder="gsk_...", |
|
|
type="password", |
|
|
value="" |
|
|
) |
|
|
else: |
|
|
gr.Markdown(""" |
|
|
<div class="success-box"> |
|
|
✅ <strong>سیستم آماده است</strong> - کلید API تنظیم شده |
|
|
</div> |
|
|
""") |
|
|
api_key_input = gr.Textbox(visible=False) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=1): |
|
|
input_text = gr.Textbox( |
|
|
label="📝 متن ورودی", |
|
|
placeholder="متن خود را اینجا وارد کنید...", |
|
|
lines=10, |
|
|
max_lines=20 |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
anonymize_btn = gr.Button( |
|
|
"🔒 ناشناسسازی متن", |
|
|
variant="primary", |
|
|
size="lg" |
|
|
) |
|
|
clear_btn = gr.Button( |
|
|
"🗑️ پاک کردن", |
|
|
variant="secondary" |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
output_text = gr.Textbox( |
|
|
label="🎯 متن ناشناسسازی شده", |
|
|
lines=10, |
|
|
max_lines=20, |
|
|
elem_classes=["result-box"] |
|
|
) |
|
|
|
|
|
|
|
|
copy_btn = gr.Button( |
|
|
"📋 کپی متن", |
|
|
variant="secondary", |
|
|
size="sm" |
|
|
) |
|
|
|
|
|
|
|
|
copy_output = gr.Textbox( |
|
|
label="📋 متن برای کپی (Ctrl+A و Ctrl+C)", |
|
|
lines=3, |
|
|
max_lines=10, |
|
|
visible=False, |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
statistics_output = gr.Markdown(label="📊 آمار تشخیص") |
|
|
with gr.Column(): |
|
|
usage_output = gr.Markdown(label="⚡ اطلاعات پردازش") |
|
|
|
|
|
entities_output = gr.Markdown(label="📋 جزئیات تغییرات") |
|
|
|
|
|
def process_text(text: str, api_key_manual: str = ""): |
|
|
"""پردازش متن""" |
|
|
|
|
|
if api_key_manual is None: |
|
|
api_key_manual = "" |
|
|
|
|
|
final_api_key = api_key_manual.strip() if api_key_manual and api_key_manual.strip() else os.getenv("GROQ_API_KEY") |
|
|
|
|
|
if not final_api_key: |
|
|
return ( |
|
|
"", |
|
|
"❌ کلید API وارد نشده است. لطفاً در فیلد بالا وارد کنید یا در Settings تنظیم کنید.", |
|
|
"", |
|
|
"" |
|
|
) |
|
|
|
|
|
if not text.strip(): |
|
|
return ( |
|
|
"", |
|
|
"❌ لطفاً متن ورودی را وارد کنید", |
|
|
"", |
|
|
"" |
|
|
) |
|
|
|
|
|
try: |
|
|
anonymizer = GroqAnonymizer(api_key=final_api_key) |
|
|
result = anonymizer.anonymize_text(text) |
|
|
|
|
|
if not result["success"]: |
|
|
return ( |
|
|
"", |
|
|
f"❌ خطا: {result['error']}", |
|
|
"", |
|
|
"" |
|
|
) |
|
|
|
|
|
|
|
|
stats = result.get("statistics", {}) |
|
|
stats_md = "📊 **آمار تشخیص:**\n\n" |
|
|
total = sum(stats.values()) if stats else 0 |
|
|
stats_md += f"🔢 **کل موارد:** {total}\n\n" |
|
|
|
|
|
type_names = { |
|
|
'company': 'شرکتها', |
|
|
'person': 'افراد', |
|
|
'group': 'گروهها', |
|
|
'amount': 'مبالغ', |
|
|
'percent': 'درصدها' |
|
|
} |
|
|
|
|
|
if stats: |
|
|
for key, value in stats.items(): |
|
|
if value > 0: |
|
|
name = type_names.get(key, key) |
|
|
stats_md += f"• {name}: **{value}** مورد\n" |
|
|
|
|
|
|
|
|
usage = result.get("usage", {}) |
|
|
usage_md = "⚡ **اطلاعات پردازش:**\n\n" |
|
|
if usage: |
|
|
usage_md += f"• مدل: Llama 3.1\n" |
|
|
usage_md += f"• Token های ورودی: {usage.get('prompt_tokens', 'نامشخص')}\n" |
|
|
usage_md += f"• Token های خروجی: {usage.get('completion_tokens', 'نامشخص')}\n" |
|
|
else: |
|
|
usage_md += "✅ پردازش با موفقیت انجام شد" |
|
|
|
|
|
|
|
|
entities = result.get("entities", []) |
|
|
entities_md = "📋 **جزئیات تغییرات:**\n\n" |
|
|
|
|
|
if entities: |
|
|
for i, entity in enumerate(entities, 1): |
|
|
entities_md += f"**{i}.** *{entity.get('type', 'نامشخص')}*\n" |
|
|
entities_md += f" • اصل: `{entity.get('original', '')}`\n" |
|
|
entities_md += f" • جایگزین: `{entity.get('anonymized', '')}`\n\n" |
|
|
else: |
|
|
entities_md += "هیچ تغییری انجام نشده یا جزئیات در دسترس نیست" |
|
|
|
|
|
return ( |
|
|
result["anonymized_text"], |
|
|
stats_md, |
|
|
usage_md, |
|
|
entities_md |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
return ( |
|
|
"", |
|
|
f"❌ خطای غیرمنتظره: {str(e)}", |
|
|
"", |
|
|
"" |
|
|
) |
|
|
|
|
|
def copy_text(text_to_copy): |
|
|
"""تابع کپی متن""" |
|
|
if not text_to_copy.strip(): |
|
|
return gr.Textbox(visible=False), "⚠️ متنی برای کپی وجود ندارد" |
|
|
|
|
|
|
|
|
return gr.Textbox(value=text_to_copy, visible=True), "✅ متن در کادر زیر آماده کپی است (Ctrl+A سپس Ctrl+C)" |
|
|
|
|
|
def clear_all(): |
|
|
"""پاک کردن تمام فیلدها""" |
|
|
return "", "", "", "", "", gr.Textbox(visible=False) |
|
|
|
|
|
|
|
|
anonymize_btn.click( |
|
|
fn=process_text, |
|
|
inputs=[input_text, api_key_input], |
|
|
outputs=[output_text, statistics_output, usage_output, entities_output] |
|
|
) |
|
|
|
|
|
clear_btn.click( |
|
|
fn=clear_all, |
|
|
outputs=[input_text, output_text, statistics_output, usage_output, entities_output, copy_output] |
|
|
) |
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
["ایران خودرو در اسفندماه سال 1402 حدود 23 هزار و 296 میلیارد تومان درآمد کسب کرد که در مقایسه با بهمن 4.58 درصد افزایش داشت."], |
|
|
["مهدی اخوان بهابادی، مدیرعامل همراه اول، اعلام کرد درآمد عملیاتی شرکت با رشد 37 درصدی به 70 هزار و 677 میلیارد تومان رسیده است."], |
|
|
["بانک پاسارگاد با شناسایی سود خالص 155 هزار میلیارد ریالی در رده دوم سودآورترین بانکهای کشور قرار گرفت."], |
|
|
["شرکت سرمایهگذاری دارویی تأمین (تیپیکو) گزارش فعالیت هیئتمدیره خود را برای سال مالی منتهی به 31 اردیبهشت 1404 منتشر کرد."] |
|
|
], |
|
|
inputs=input_text, |
|
|
label="📚 مثالهای آزمایشی" |
|
|
) |
|
|
|
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### 🎯 **ویژگیها:** |
|
|
- 🏢 تشخیص نام شرکتها و سازمانها |
|
|
- 👤 تشخیص نام افراد با پیشوندهای مختلف |
|
|
- 💰 تشخیص مبالغ مالی و اعداد |
|
|
- 📊 تشخیص درصدها با فرمتهای مختلف |
|
|
- 🏗️ تشخیص نام گروهها |
|
|
|
|
|
### 📝 **نحوه کار:** |
|
|
1. متن خود را در کادر ورودی وارد کنید |
|
|
2. دکمه "ناشناسسازی متن" را کلیک کنید |
|
|
3. نتیجه را در کادر خروجی مشاهده کنید |
|
|
4. آمار و جزئیات تغییرات را بررسی کنید |
|
|
|
|
|
### ⚠️ **توجه:** |
|
|
- این سیستم برای متون فارسی بهینهسازی شده است |
|
|
- حداکثر طول متن ورودی حدود 1000 کلمه توصیه میشود |
|
|
- اطلاعات شما پس از پردازش حذف میشوند |
|
|
""") |
|
|
|
|
|
return interface |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
interface = create_interface() |
|
|
interface.launch() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
gradio==4.0.0 |
|
|
requests==2.31.0 |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
--- |
|
|
title: Persian Text Anonymizer |
|
|
emoji: 🔒 |
|
|
colorFrom: blue |
|
|
colorTo: purple |
|
|
sdk: gradio |
|
|
sdk_version: 4.0.0 |
|
|
app_file: app.py |
|
|
pinned: false |
|
|
license: mit |
|
|
--- |
|
|
|
|
|
# 🔒 سیستم ناشناسسازی متن فارسی |
|
|
|
|
|
سیستم هوشمند برای تشخیص و ناشناسسازی اطلاعات حساس در متون فارسی با استفاده از مدلهای زبانی پیشرفته. |
|
|
|
|
|
## ویژگیها |
|
|
|
|
|
- 🏢 **تشخیص شرکتها**: نام شرکتها، بانکها، موسسات |
|
|
- 👤 **تشخیص افراد**: نام اشخاص با پیشوندهای مختلف |
|
|
- 💰 **تشخیص مبالغ**: اعداد، مبالغ مالی، واحدهای اندازهگیری |
|
|
- 📊 **تشخیص درصدها**: انواع فرمتهای درصد |
|
|
- 🏗️ **تشخیص گروهها**: نام گروهها و هلدینگها |
|
|
|
|
|
## تنظیمات |
|
|
|
|
|
برای استفاده از این برنامه، نیاز به تنظیم متغیر محیطی `GROQ_API_KEY` دارید. |
|
|
|
|
|
## مثال استفاده |
|
|
|
|
|
```python |
|
|
from app import GroqAnonymizer |
|
|
|
|
|
anonymizer = GroqAnonymizer() |
|
|
result = anonymizer.anonymize_text("احمد رضایی مدیرعامل شرکت پارس 100 میلیون تومان درآمد دارد.") |
|
|
print(result["anonymized_text"]) |
|
|
# خروجی: "person-01 مدیرعامل company-01 amount-01 درآمد دارد." |
|
|
``` |
|
|
|
|
|
## تکنولوژی |
|
|
|
|
|
- **Groq API**: برای پردازش هوشمند متن |
|
|
- **Gradio**: برای رابط کاربری وب |
|
|
- **Llama 3.1**: مدل زبانی پیشرفته |
|
|
|
|
|
## مجوز |
|
|
|
|
|
MIT License |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
__pycache__/ |
|
|
*.py[cod] |
|
|
*$py.class |
|
|
*.so |
|
|
.Python |
|
|
build/ |
|
|
develop-eggs/ |
|
|
dist/ |
|
|
downloads/ |
|
|
eggs/ |
|
|
.eggs/ |
|
|
lib/ |
|
|
lib64/ |
|
|
parts/ |
|
|
sdist/ |
|
|
var/ |
|
|
wheels/ |
|
|
*.egg-info/ |
|
|
.installed.cfg |
|
|
*.egg |
|
|
MANIFEST |
|
|
|
|
|
.env |
|
|
.venv |
|
|
env/ |
|
|
venv/ |
|
|
ENV/ |
|
|
env.bak/ |
|
|
venv.bak/ |
|
|
|
|
|
.DS_Store |
|
|
.vscode/ |
|
|
.idea/ |
|
|
*.swp |
|
|
*.swo |
|
|
|
|
|
gradio_cached_examples/ |
|
|
flagged/ |
|
|
""" |