|
|
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 یافت نشد") |
|
|
|
|
|
self.config = GroqConfig(api_key=api_key) |
|
|
self.system_prompt = self._create_system_prompt() |
|
|
|
|
|
def _create_system_prompt(self) -> str: |
|
|
"""ایجاد دستورالعمل سیستمی برای Groq""" |
|
|
return """Replace these in Persian text: |
|
|
- Company names with: company-01, company-02, etc. |
|
|
- Person names with: person-01, person-02, etc. |
|
|
- Numbers/amounts with: amount-01, amount-02, etc. |
|
|
- Percentages with: percent-01, percent-02, etc. |
|
|
|
|
|
Only return the anonymized text, nothing else.""" |
|
|
|
|
|
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: |
|
|
response = requests.post( |
|
|
f"{self.config.base_url}/chat/completions", |
|
|
headers=headers, |
|
|
json=payload, |
|
|
timeout=30 |
|
|
) |
|
|
response.raise_for_status() |
|
|
return response.json() |
|
|
|
|
|
except requests.exceptions.RequestException as 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(): |
|
|
"""ایجاد رابط کاربری""" |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
.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 api_key_available: |
|
|
gr.Markdown(""" |
|
|
<div class="success-box"> |
|
|
✅ <strong>سیستم آماده است</strong> - کلید API تنظیم شده |
|
|
</div> |
|
|
""") |
|
|
api_key_input = gr.Textbox(visible=False, value="") |
|
|
else: |
|
|
gr.Markdown(""" |
|
|
<div class="warning-box"> |
|
|
⚠️ <strong>کلید API تنظیم نشده</strong><br> |
|
|
لطفاً کلید Groq API خود را در زیر وارد کنید |
|
|
</div> |
|
|
""") |
|
|
api_key_input = gr.Textbox( |
|
|
label="🔑 کلید Groq API", |
|
|
placeholder="gsk_...", |
|
|
type="password", |
|
|
value="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" |
|
|
) |
|
|
|
|
|
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 = "" |
|
|
if api_key_manual and api_key_manual.strip(): |
|
|
final_api_key = api_key_manual.strip() |
|
|
elif os.getenv("GROQ_API_KEY"): |
|
|
final_api_key = os.getenv("GROQ_API_KEY") |
|
|
|
|
|
if not final_api_key: |
|
|
return ( |
|
|
"", |
|
|
"❌ کلید API وارد نشده است", |
|
|
"", |
|
|
"" |
|
|
) |
|
|
|
|
|
if not text or 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_md = "📋 **جزئیات تغییرات:**\n\n" |
|
|
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 or not text_to_copy.strip(): |
|
|
return gr.Textbox(visible=False), "⚠️ متنی برای کپی وجود ندارد" |
|
|
|
|
|
return gr.Textbox(value=text_to_copy, visible=True), "✅ متن در کادر زیر آماده کپی است" |
|
|
|
|
|
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] |
|
|
) |
|
|
|
|
|
copy_btn.click( |
|
|
fn=copy_text, |
|
|
inputs=[output_text], |
|
|
outputs=[copy_output, statistics_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 هزار میلیارد ریالی در رده دوم سودآورترین بانکهای کشور قرار گرفت."] |
|
|
], |
|
|
inputs=input_text, |
|
|
label="📚 مثالهای آزمایشی" |
|
|
) |
|
|
|
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### 🎯 **ویژگیها:** |
|
|
- 🏢 تشخیص نام شرکتها و سازمانها |
|
|
- 👤 تشخیص نام افراد با پیشوندهای مختلف |
|
|
- 💰 تشخیص مبالغ مالی و اعداد |
|
|
- 📊 تشخیص درصدها با فرمتهای مختلف |
|
|
""") |
|
|
|
|
|
return interface |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
interface = create_interface() |
|
|
interface.launch() |
|
|
|