|
|
import gradio as gr |
|
|
import re |
|
|
import os |
|
|
import requests |
|
|
import logging |
|
|
from typing import Dict, List, Tuple, Set |
|
|
import json |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
class AnonymizerCerebrasEnhanced: |
|
|
def __init__(self, api_key: str = None): |
|
|
self.api_key = api_key or os.getenv("CEREBRAS_API_KEY") |
|
|
self.mapping_table = {} |
|
|
self.counters = { |
|
|
'company': 0, 'person': 0, 'amount': 0, 'phone': 0, |
|
|
'email': 0, 'id_number': 0, 'date': 0, 'location': 0, |
|
|
'percent': 0 |
|
|
} |
|
|
self.seen_entities = {} |
|
|
|
|
|
if not self.api_key: |
|
|
raise ValueError("❌ کلید API Cerebras یافت نشد!") |
|
|
|
|
|
logger.info("✅ Anonymizer Enhanced مقداردهی شد") |
|
|
|
|
|
def get_system_prompt(self) -> str: |
|
|
"""ایجاد دستورالعمل سیستمی پیشرفته برای Groq""" |
|
|
return """شما یک «ناشناسساز متون مالی/خبری فارسی» هستید. وظیفهتان جایگزینی اسامی خاص و مقادیر عددی با شناسههای بیمعناست. |
|
|
|
|
|
## **قوانین اندیسگذاری - CRITICAL** |
|
|
### **1. ترتیب شمارهگذاری الزامی:** |
|
|
- شرکتها: company-01, company-02, company-03, company-04, ... (پیوسته و بدون گپ) |
|
|
- اشخاص: person-01, person-02, person-03, ... (پیوسته و بدون گپ) |
|
|
- اعداد: amount-01, amount-02, amount-03, ... (پیوسته و بدون گپ) |
|
|
- درصدها: percent-01, percent-02, percent-03, ... (پیوسته و بدون گپ) |
|
|
- تاریخها: date-01, date-02, date-03, ... (پیوسته و بدون گپ) |
|
|
|
|
|
### **2. ثبات شناسهها در متن - MUST MAINTAIN:** |
|
|
- اگر "همراه اول" اولبار company-01 شد، در تمام متن همان باشد |
|
|
- اگر "مهدی احمدی" اولبار person-01 شد، در تمام متن همان باشد |
|
|
- **CRITICAL: اگر "سروش خسروی" = person-01، تو "خسروی" تنهایی هم = person-01 باشد** |
|
|
|
|
|
### **3. تشخیص صحیح انواع:** |
|
|
**شرکت/سازمان:** همراه اول، بانک ملی، ایرانخودرو، سایپا، بانک مرکزی، سامانه کدال، وزارت نفت |
|
|
**شخص:** مهدی اخوان بهابادی، محمدرضا فرزین، ابوالفضل نجارزاده، سروش خسروی |
|
|
**عدد:** 37، 70، 677، 73.7، 178 (هر عددی) |
|
|
**درصد:** 37 درصدی، 15 درصدی، 53 درصد، 43% |
|
|
**تاریخ:** 1403، 1404، اردیبهشت، فروردین، 30 آذر 1403 |
|
|
|
|
|
## **انواع موجودیتها:** |
|
|
|
|
|
**company-XX:** نام شرکتها، سازمانها، بانکها، هلدینگها، گروههای مالی |
|
|
|
|
|
**person-XX:** نام و نام خانوادگی اشخاص - شامل نام کامل، نام کوچک تنهایی، نام خانوادگی تنهایی |
|
|
|
|
|
**amount-XX:** مبالغ مالی شامل ریال، تومان، همت، دلار، تن، دستگاه و واحدهای اندازهگیری |
|
|
|
|
|
**percent-XX:** درصدها و نسبتها |
|
|
|
|
|
**date-XX:** تمام تاریخها شامل سال، ماہ، روز و ترکیب آنها |
|
|
|
|
|
## **قوانین کلیدی:** |
|
|
|
|
|
1. **ترتیب شمارهگذاری:** اولین باری که موجودیت ظاهر میشود، شماره میگیرد (01، 02، 03، ...) |
|
|
|
|
|
2. **حفظ هویت یکسان:** اگر همان موجودیت دوباره آمد، از همان شماره استفاده کن. |
|
|
|
|
|
3. **CRITICAL - Entity Linking برای اشخاص:** |
|
|
- اگر "سروش خسروی" = person-01 شد، تو "خسروی" تنهایی = person-01 |
|
|
- اگر "محمدرضا فرزین" = person-01 شد، تو "فرزین" یا "محمدرضا" = person-01 |
|
|
- اگر "علی احمدی" = person-01 شد، تو "احمدی"، "علی"، "آن شخص" همه = person-01 |
|
|
- **MUST TRACK: نام کامل → نام کوچک → نام خانوادگی → ضمیرها** |
|
|
- **نام خانوادگی تنهایی را هرگز بدون linking رها نکن** |
|
|
|
|
|
4. **تشخیص نامهای مختلف:** "فولاد مبارکه اصفهان" و "فولاد مبارکه" و "این شرکت" همه company-01 هستند. |
|
|
|
|
|
5. **CRITICAL - تمام تاریخها باید Anonymize شوند:** |
|
|
- سال ONLY: "سال 1403" → "سال date-01" |
|
|
- ماہ ONLY: "اردیبهشت" → "date-02" |
|
|
- سال + ماہ: "اردیبهشت 1404" → "date-03 date-04" |
|
|
- تاریخ مکمل: "1403/04/12" → "date-05/date-06/date-07" |
|
|
- **NO EXCEPTION: تمام اعداد تاریخ باید anonymize شوند** |
|
|
- **یکسانی برقرار کن: اگر "1403" یک جا date-01 شد، همه جا date-01 باشد** |
|
|
|
|
|
6. **مبالغ و درصدهای مختلف:** هر عدد جدید، شماره جدید میگیرد |
|
|
|
|
|
7. **حفظ ساختار:** ساختار جمله را حفظ کن، کلمات توصیفی مثل "شرکت"، "بانک"، "گروه" را قبل از برچسب حفظ کن |
|
|
|
|
|
8. **هیچ توضیح اضافهای نده:** فقط متن ناشناسشده را برگردان |
|
|
|
|
|
## **موارد حفظ شده:** |
|
|
- عناوین شغلی: مدیرعامل، رئیس کل، مدیرکل، سرپرست |
|
|
- واحدها: میلیارد تومان، همت، ریال، ماه، سال |
|
|
- مکانها: تهران، اصفهان، ایران |
|
|
- کلمات توضیحی: "شرکت"، "بانک"، "گروه" |
|
|
|
|
|
## **ممنوع:** |
|
|
- کلمات انگلیسی اضافی |
|
|
- تغییر ساختار جمله |
|
|
- حذف یا اضافه کردن کلمات |
|
|
- **نام خانوادگی یا نام کوچک تنهایی را بدون linking رها کردن** |
|
|
|
|
|
## **نمونههای آموزشی:** |
|
|
|
|
|
**نمونه ۱ - Entity Linking برای نامها (CRITICAL):** |
|
|
ورودی: سروش خسروی، سرپرست هیأتمدیره. خسروی اعلام کرد که سود خالص 216 میلیارد تومان بود. خسروی همچنین به چالشها اشاره کرد. |
|
|
خروجی: person-01، سرپرست هیأتمدیره. person-01 اعلام کرد که سود خالص amount-01 بود. person-01 همچنین به چالشها اشاره کرد. |
|
|
|
|
|
**نمونه ۲ - تمام تاریخها Anonymize شوند (CRITICAL):** |
|
|
ورودی: سال 1403 یکی از سختترین سالها برای صنعت پتروشیمی بود. در اردیبهشت 1404 شرکت گزارش منتشر کرد و کاهش سود خالص در سال 1403 را اعلام کرد. |
|
|
خروجی: سال date-01 یکی از سختترین سالها برای صنعت پتروشیمی بود. در date-02 date-03 شرکت گزارش منتشر کرد و کاهش سود خالص در سال date-01 را اعلام کرد. |
|
|
|
|
|
**نمونه ۳:** |
|
|
ورودی: مهدی اخوان بهابادی، مدیرعامل همراه اول، اعلام کرد درآمد عملیاتی شرکت با رشد 37 درصدی به 70 هزار و 677 میلیارد تومان رسیده است. سود خالص 7101 میلیارد تومان و تلفیقی گروه همراه اول 8003 میلیارد تومان شد. |
|
|
خروجی: person-01، مدیرعامل company-01، اعلام کرد درآمد عملیاتی شرکت با رشد percent-01 به amount-01 رسیده است. سود خالص amount-02 و تلفیقی گروه company-01 amount-03 شد. |
|
|
|
|
|
**نمونه ۴:** |
|
|
ورودی: بانک مرکزی و بانک ملی با همکاری محمدرضا فرزین، 60 درصد سپردهها را مدیریت کردند. |
|
|
خروجی: company-01 و company-02 با همکاری person-01، percent-01 سپردهها را مدیریت کردند. |
|
|
|
|
|
**نمونه ۵:** |
|
|
ورودی: سایپا و ایرانخودرو مجموع زیان 620 همت داشتند و سایپا 269 هزار میلیارد زیان اعلام کرد. |
|
|
خروجی: company-01 و company-02 مجموع زیان amount-01 داشتند و company-01 amount-02 زیان اعلام کرد. |
|
|
|
|
|
**نمونه ۶ - تاریخ مکمل:** |
|
|
ورودی: مجمع عمومی مورخ 1403/04/12 برگزار شد و گزارش مالی منتهی به 30 آذر 1403 تصویب رسید. |
|
|
خروجی: مجمع عمومی مورخ date-01/date-02/date-03 برگزار شد و گزارش مالی منتهی به date-04 date-05 date-06 تصویب رسید. |
|
|
|
|
|
**نمونه ۷:** |
|
|
ورودی: بانک پاسارگاد با شناسایی سود خالص 155 هزار میلیارد ریالی در رده دوم سودآورترین بانکهای کشور قرار گرفت. در مقابل، بانک سرمایه با مدیرعاملی فرجاله قدمی وضعیت بحرانی دارد. |
|
|
خروجی: company-01 با شناسایی سود خالص amount-01 در رده دوم سودآورترین بانکهای کشور قرار گرفت. در مقابل، company-02 با مدیرعاملی person-01 وضعیت بحرانی دارد. |
|
|
|
|
|
**نمونه ۸:** |
|
|
ورودی: بانک سرمایه با مدیرعاملی فرجاله قدمی زیان خالص 2700 میلیارد تومانی در سهماهه نخست 1404 گزارش کرد. نسبت کفایت سرمایه به منفی 345 درصد رسیده و زیان انباشته نزدیک به 67 هزار میلیارد تومان است. |
|
|
خروجی: company-01 با مدیرعاملی person-01 زیان خالص amount-01 در سهماهه نخست date-01 گزارش کرد. نسبت کفایت سرمایه به منفی percent-01 رسیده و زیان انباشته نزدیک به amount-02 است. |
|
|
|
|
|
**نمونه ۹:** |
|
|
ورودی: دو بانک ملت و پاسارگاد به ترتیب با شناسایی سود خالص 157 و 155 هزار میلیارد ریالی رقابت تنگاتنگی داشته و در ردههای اول و دوم جای دارند. |
|
|
خروجی: دو بانک company-01 و company-02 به ترتیب با شناسایی سود خالص amount-01 و amount-02 رقابت تنگاتنگی داشته و در ردههای اول و دوم جای دارند. |
|
|
|
|
|
**نمونه ۱۰:** |
|
|
ورودی: مرور صورتهای مالی بانکها نشان میدهد سهم سودهای ارزی بهراحتی به 40–60٪ رسیده است و این مسئله نشاندهنده وضعیت غیرعادی بازار است. |
|
|
خروجی: مرور صورتهای مالی بانکها نشان میدهد سهم سودهای ارزی بهراحتی به percent-01 رسیده است و این مسئله نشاندهنده وضعیت غیرعادی بازار است. |
|
|
|
|
|
**فقط متن ناشناسشده را برگردان - هیچ توضیح اضافی نیاز نیست.""" |
|
|
|
|
|
def get_user_prompt(self, text: str) -> str: |
|
|
"""تشکیل پرامپت کاربر""" |
|
|
return f"""متن مالی فارسی زیر را تجزیه و تحلیل کنید. تمام موجودیتهای حساس را شناسایی کنید و یک JSON Array برگردانید. |
|
|
|
|
|
متن: |
|
|
{text} |
|
|
|
|
|
**مهم**: |
|
|
- اگر چند بار یک نام تکرار شود، یک id بدهید |
|
|
- کلمات عمومی را حفظ کنید |
|
|
- واحدها را حفظ کنید |
|
|
- فقط JSON برگردانید! |
|
|
|
|
|
یک JSON Array برگردانید. هر عنصر دارای: |
|
|
- "text": متن دقیق استخراج شده |
|
|
- "type": نوع (company, person, amount, percent, phone, email, date, location, id_number) |
|
|
- "original": توضیح اضافی اگر نام مستعار باشد""" |
|
|
|
|
|
def call_cerebras(self, text: str) -> List[Dict]: |
|
|
"""فراخوانی Cerebras API با پرامپت بهبود شده""" |
|
|
logger.info("🔄 فراخوانی Cerebras API با دستورالعمل قوی...") |
|
|
|
|
|
system_prompt = self.get_system_prompt() |
|
|
user_prompt = self.get_user_prompt(text) |
|
|
|
|
|
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": system_prompt}, |
|
|
{"role": "user", "content": user_prompt} |
|
|
], |
|
|
"max_tokens": 4000, |
|
|
"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: |
|
|
|
|
|
content = content.replace("```json", "").replace("```", "").strip() |
|
|
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: {content[:200]}") |
|
|
return [] |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ خطا Cerebras: {e}") |
|
|
return [] |
|
|
|
|
|
def get_placeholder(self, entity_type: str) -> str: |
|
|
"""تولید placeholder با format جدید""" |
|
|
type_lower = entity_type.lower() |
|
|
if type_lower not in self.counters: |
|
|
type_lower = 'amount' |
|
|
|
|
|
self.counters[type_lower] += 1 |
|
|
return f"{type_lower}-{self.counters[type_lower]:02d}" |
|
|
|
|
|
def anonymize(self, text: str) -> Tuple[str, List]: |
|
|
"""ناشناسسازی متن با قوانین ثبات""" |
|
|
logger.info("🚀 شروع ناشناسسازی متن...") |
|
|
|
|
|
|
|
|
self.mapping_table = {} |
|
|
self.seen_entities = {} |
|
|
for key in self.counters: |
|
|
self.counters[key] = 0 |
|
|
|
|
|
|
|
|
entities = self.call_cerebras(text) |
|
|
|
|
|
if not entities: |
|
|
logger.warning("⚠️ موجودیتی شناسایی نشد") |
|
|
return text, [] |
|
|
|
|
|
logger.info("🔄 Processing entities...") |
|
|
|
|
|
|
|
|
anonymized = text |
|
|
replacements = [] |
|
|
|
|
|
for entity in entities: |
|
|
entity_type = entity.get('type', 'amount').lower() |
|
|
entity_text = entity.get('text', '').strip() |
|
|
original_info = entity.get('original', '') |
|
|
|
|
|
if not entity_text: |
|
|
continue |
|
|
|
|
|
|
|
|
entity_key = (entity_type, entity_text.lower()) |
|
|
|
|
|
if entity_key in self.seen_entities: |
|
|
token = self.seen_entities[entity_key] |
|
|
logger.info(f"🔄 موجودیت تکراری: {entity_text} → {token}") |
|
|
else: |
|
|
token = self.get_placeholder(entity_type) |
|
|
self.seen_entities[entity_key] = token |
|
|
self.mapping_table[token] = { |
|
|
'original': entity_text, |
|
|
'type': entity_type, |
|
|
'note': original_info |
|
|
} |
|
|
logger.info(f"✅ جایگزینی: {entity_text} → {token}") |
|
|
|
|
|
|
|
|
idx = anonymized.find(entity_text) |
|
|
if idx != -1: |
|
|
anonymized = anonymized[:idx] + token + anonymized[idx + len(entity_text):] |
|
|
replacements.append({ |
|
|
'original': entity_text, |
|
|
'placeholder': token, |
|
|
'type': entity_type |
|
|
}) |
|
|
|
|
|
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, info in sorted(self.mapping_table.items()): |
|
|
entity_type = info.get('type', 'unknown') |
|
|
original = info.get('original', '') |
|
|
note = info.get('note', '') |
|
|
|
|
|
note_str = f" ({note})" if note else "" |
|
|
result += f"| `{token}` | {original}{note_str} | {entity_type} |\n" |
|
|
|
|
|
return result |
|
|
|
|
|
def restore(self, text: str) -> str: |
|
|
"""بازگردانی اطلاعات اصلی""" |
|
|
logger.info("🔄 بازگردانی اطلاعات...") |
|
|
restored = text |
|
|
for token, info in self.mapping_table.items(): |
|
|
original = info.get('original', '') |
|
|
restored = restored.replace(token, original) |
|
|
logger.info("✅ بازگردانی کامل") |
|
|
return restored |
|
|
|
|
|
|
|
|
|
|
|
anonymizer = None |
|
|
|
|
|
def process(input_text: str) -> Tuple[str, str, str, str, str]: |
|
|
""" |
|
|
روند کامل: |
|
|
1. ناشناسسازی با Cerebras (llama-3.3-70b) + پرامپت قوی |
|
|
2. ارسال به ChatGPT (حتما!) |
|
|
3. بازگردانی پاسخ ChatGPT |
|
|
""" |
|
|
global anonymizer |
|
|
|
|
|
try: |
|
|
if not input_text.strip(): |
|
|
return "", "", "", "", "" |
|
|
|
|
|
|
|
|
api_key_cerebras = os.getenv("CEREBRAS_API_KEY") |
|
|
api_key_gpt = os.getenv("OPENAI_API_KEY") |
|
|
|
|
|
if not api_key_gpt: |
|
|
logger.error("❌ OPENAI_API_KEY یافت نشد") |
|
|
return "", "", "", "", "" |
|
|
|
|
|
if not api_key_cerebras: |
|
|
logger.error("❌ CEREBRAS_API_KEY یافت نشد") |
|
|
return "", "", "", "", "" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not anonymizer: |
|
|
logger.info("Initializing anonymizer...") |
|
|
anonymizer = AnonymizerCerebrasEnhanced() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("Step 1: Anonymizing text with Cerebras...") |
|
|
|
|
|
anonymized_text, entities = anonymizer.anonymize(input_text) |
|
|
|
|
|
if not entities: |
|
|
logger.warning("⚠️ موجودیتی شناسایی نشد - متن ناشناس نشد") |
|
|
return input_text, "", "", "", "" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("Step 2: Creating mapping table") |
|
|
mapping = anonymizer.get_mapping_table_str() |
|
|
logger.info(f"📋 {len(anonymizer.mapping_table)} نگاشت ایجاد شد") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("Step 3: Sending to ChatGPT...") |
|
|
|
|
|
prompt = f"""متن ناشناسشده زیر (متن مالی) را تحلیل و خلاصه کنید. |
|
|
|
|
|
متن: |
|
|
{anonymized_text} |
|
|
|
|
|
لطفاً: |
|
|
1. خلاصهای مختصر و معنادار ارائه دهید |
|
|
2. نکات اصلی را مشخص کنید |
|
|
3. تمام توکنهای ناشناس (مثل company-01، amount-02) را حفظ کنید |
|
|
4. تنها اطلاعات موجود در متن را بیان کنید""" |
|
|
|
|
|
logger.info(f"📤 ارسال به ChatGPT (gpt-4o-mini)...") |
|
|
|
|
|
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": 1500, |
|
|
"temperature": 0.7 |
|
|
}, |
|
|
timeout=30 |
|
|
) |
|
|
|
|
|
if gpt_response_obj.status_code == 200: |
|
|
gpt_response = gpt_response_obj.json()['choices'][0]['message']['content'] |
|
|
logger.info("✅ پاسخ دریافت شد") |
|
|
else: |
|
|
error_text = gpt_response_obj.json().get('error', {}).get('message', gpt_response_obj.text) |
|
|
logger.error(f"❌ خطای ChatGPT: {error_text}") |
|
|
return input_text, anonymized_text, "", "", mapping |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ خطا در ارسال به ChatGPT: {e}") |
|
|
return input_text, anonymized_text, "", "", mapping |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.info("Step 4: Restoring original text...") |
|
|
|
|
|
restored_text = anonymizer.restore(gpt_response) |
|
|
|
|
|
logger.info(f"✅ بازگردانی کامل") |
|
|
|
|
|
logger.info(f"Done. Input: {len(input_text)} | Anonymized: {len(anonymized_text)} | Entities: {len(entities)}") |
|
|
|
|
|
return input_text, anonymized_text, gpt_response, restored_text, mapping |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"❌ خطا عمومی: {e}", exc_info=True) |
|
|
return "", "", "", "", "" |
|
|
|
|
|
def clear(): |
|
|
"""پاک کردن""" |
|
|
return "", "", "", "", "" |
|
|
|
|
|
|
|
|
with gr.Blocks(title="Text Anonymization", theme=gr.themes.Soft()) as app: |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=2): |
|
|
input_text = gr.Textbox( |
|
|
lines=12, |
|
|
placeholder="متن را وارد کنید...", |
|
|
label="Input" |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
process_btn = gr.Button("Process", variant="primary", size="lg") |
|
|
clear_btn = gr.Button("Clear", variant="stop") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
anonymized_text = gr.Textbox( |
|
|
lines=10, |
|
|
label="Anonymized", |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
with gr.Column(): |
|
|
gpt_response = gr.Textbox( |
|
|
lines=10, |
|
|
label="GPT Response", |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
with gr.Column(): |
|
|
restored_text = gr.Textbox( |
|
|
lines=10, |
|
|
label="Restored", |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
mapping = gr.Textbox( |
|
|
lines=10, |
|
|
label="Mapping", |
|
|
interactive=False |
|
|
) |
|
|
|
|
|
|
|
|
process_btn.click( |
|
|
fn=process, |
|
|
inputs=[input_text], |
|
|
outputs=[input_text, anonymized_text, gpt_response, restored_text, mapping] |
|
|
) |
|
|
|
|
|
clear_btn.click( |
|
|
fn=clear, |
|
|
outputs=[input_text, anonymized_text, gpt_response, restored_text, mapping] |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("Starting Text Anonymization System...") |
|
|
app.launch( |
|
|
server_name="0.0.0.0", |
|
|
server_port=7860, |
|
|
share=False, |
|
|
show_error=True |
|
|
) |
|
|
|