leilaghomashchi commited on
Commit
86a84d0
·
verified ·
1 Parent(s): 344b293

Upload app_enhanced (3).py

Browse files
Files changed (1) hide show
  1. app_enhanced (3).py +506 -0
app_enhanced (3).py ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import re
3
+ import os
4
+ import requests
5
+ import logging
6
+ from typing import Dict, List, Tuple, Set
7
+ import json
8
+
9
+ logging.basicConfig(level=logging.INFO)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class AnonymizerCerebrasEnhanced:
13
+ def __init__(self, api_key: str = None):
14
+ self.api_key = api_key or os.getenv("CEREBRAS_API_KEY")
15
+ self.mapping_table = {}
16
+ self.counters = {
17
+ 'company': 0, 'person': 0, 'amount': 0, 'phone': 0,
18
+ 'email': 0, 'id_number': 0, 'date': 0, 'location': 0,
19
+ 'percent': 0
20
+ }
21
+ self.seen_entities = {} # برای ثبات نگاشت
22
+
23
+ if not self.api_key:
24
+ raise ValueError("❌ کلید API Cerebras یافت نشد!")
25
+
26
+ logger.info("✅ Anonymizer Enhanced مقداردهی شد")
27
+
28
+ def get_system_prompt(self) -> str:
29
+ """ایجاد دستورالعمل سیستمی بهینه شده"""
30
+ return """شما یک سیستم ناشناس‌سازی متون مالی فارسی هستید.
31
+
32
+ ⚠️ CRITICAL: در پاسخ نهایی خود، فقط و فقط متن ناشناس‌سازی شده را برگردانید، بدون هیچ توضیح، تحلیل، یا تگ اضافی.
33
+
34
+ ## قوانین اندیس‌گذاری:
35
+ 1. **ترتیب پیوسته**: company-01, company-02, ... | person-01, person-02, ... | amount-01, amount-02, ... | percent-01, percent-02, ...
36
+ 2. **ثبات**: اگر "همراه اول" → company-01 شد، در تمام متن همان باشد
37
+ 3. **نام مستعار**: "فاما" = "فولاد مبارکه" → هر دو company-01
38
+ 4. **اشاره ضمنی**: "این شرکت" اگر به company-01 اشاره دارد → company-01 (نه company-02)
39
+
40
+ ## قوانین شناسایی اسام (CRITICAL):
41
+
42
+ ### برای شخصیت (PERSON):
43
+ - **نام کامل**: "علی خسروی" → person-01
44
+ - **نام خانوادگی تنهایی**: "خسروی" اگر قبلاً "علی خسروی" دیده شده → person-01 (همان نفر)
45
+ - **نام کوچک تنهایی**: "علی" اگر قبلاً "علی خسروی" دیده شده → person-01 (همان نفر)
46
+ - اگر "خسروی" بعداً بیاید: آن را به person-01 جایگزین کن (نه person-02)
47
+ - نام‌های جزئی تکرار شونده باید به یک توکن منجر شوند
48
+
49
+ ### برای شرکت (COMPANY):
50
+ - **نام کامل**: "شرکت پتروشیمی غدیر" → company-01
51
+ - **بدون کلمه شرکت**: "پتروشیمی غدیر" اگر قبلاً "شرکت پتروشیمی غدیر" دیده شده → company-01 (همان شرکت)
52
+ - **نام اختصاری**: "پتروشیمی" اگر قبلاً "پتروشیمی غدیر" دیده شده → company-01
53
+ - **بدون پیشوند**: "غدیر" اگر قبلاً "پتروشیمی غدیر" دیده شده → company-01
54
+ - بانک، گروه، سازمان هم به عنوان شرکت شناخته شوند
55
+
56
+ ## انواع موجودیت:
57
+ - **company-XX**: شرکت‌ها، بانک‌ها، سازمان‌ها، گروه‌ها
58
+ - **person-XX**: نام و نام خانوادگی اشخاص
59
+ - **amount-XX**: مبالغ - واحد را حفظ کن
60
+ - **percent-XX**: درصدها
61
+ - **phone-XX**: شماره تلفن
62
+ - **email-XX**: آدرس ایمیل
63
+ - **date-XX**: تاریخ و دوره زمانی مشخص (نه "ماهه")
64
+ - **location-XX**: شهر، استان، کشور
65
+ - **id_number-XX**: شماره شناسایی، کد ملی
66
+
67
+ ## قوانین کلیدی:
68
+ 1. **بازرس = شرکت**: "بازرس شرکت X" → بازرس حفظ، X = company-XX
69
+ 2. **واحدها**: "amount-01 میلیارد تومان" ✅ (واحد را حفظ کن)
70
+ 3. **گروه‌ها**: "گروه X" → company-XX
71
+ 4. **کلمات عمومی حفظ**: "سه شرکت" → حفظ (فقط نام شرکت را ناشناس کن)
72
+ 5. **دوره زمانی حفظ**: "۵ ماهه" → حفظ (فقط تاریخ مشخص = date-XX)
73
+ 6. **بازه = یک entity**: "یک تا 1.5 میلیون" → amount-01
74
+ 7. **درصدها**: شناسایی تمام درصدها (خصوصاً بین 50 تا 70)
75
+ 8. **تمام ارقام**: شناسایی تمام ارقام موجود در متن به عنوان amount-XX
76
+
77
+ ## فرمت خروجی JSON:
78
+ [
79
+ {"text": "متن دقیق موجودیت", "type": "company", "original": "نام اصلی"},
80
+ {"text": "...", "type": "person", "original": "..."},
81
+ ...
82
+ ]
83
+
84
+ ✅ فقط متن ناشناس‌شده را برگردانید."""
85
+
86
+ def get_user_prompt(self, text: str) -> str:
87
+ """تشکیل پرامپت کاربر"""
88
+ return f"""متن مالی فارسی زیر را تجزیه و تحلیل کنید. تمام موجودیت‌های حساس را شناسایی کنید و یک JSON Array برگردانید.
89
+
90
+ متن:
91
+ {text}
92
+
93
+ **قوانین شناسایی مهم**:
94
+
95
+ 1. **نام‌های جزئی برای شخص**:
96
+ - اگر "علی خسروی" نام کامل بیاید
97
+ - و بعداً فقط "خسروی" یا فقط "علی" بیاید
98
+ - هر سه را یک موجودیت (person-01) بشناسید
99
+ - در JSON: {"text": "خسروی", "type": "person", "original": "علی خسروی"}
100
+
101
+ 2. **نام‌های شرکت بدون پیشوند**:
102
+ - اگر "شرکت پتروشیمی غدیر" بیاید
103
+ - و بعداً "پتروشیمی غدیر" یا فقط "پتروشیمی" یا فقط "غدیر"
104
+ - همه را یک موجودیت (company-01) بشناسید
105
+ - در JSON: {"text": "پتروشیمی غدیر", "type": "company", "original": "شرکت پتروشیمی غدیر"}
106
+
107
+ 3. **کلمات پیشوند حفظ‌شونده**:
108
+ - شرکت، بانک، گروه، سازمان را حفظ کنید
109
+ - فقط نام اصلی را ناشناس کنید
110
+
111
+ 4. **تکراری و متشابه**:
112
+ - اگر چند بار یک نام (به صورت‌های مختلف) تکرار شود، یک id بدهید
113
+ - کلمات عمومی را حفظ کنید
114
+ - واحدها را حفظ کنید
115
+
116
+ **مثال**:
117
+
118
+ ورودی: "علی محمدی و محمدی در شرکت پتروشیمی غدیر و پتروشیمی غدیر کار کردند."
119
+
120
+ خروجی JSON:
121
+ [
122
+ {"text": "علی محمدی", "type": "person", "original": ""},
123
+ {"text": "محمدی", "type": "person", "original": "علی محمدی"},
124
+ {"text": "شرکت پتروشیمی غدیر", "type": "company", "original": ""},
125
+ {"text": "پتروشیمی غدیر", "type": "company", "original": "شرکت پتروشیمی غدیر"}
126
+ ]
127
+
128
+ **مهم**:
129
+ - اگر چند بار یک نام تکرار شود، یک id بدهید
130
+ - کلمات عمومی را حفظ کنید
131
+ - واحدها را حفظ کنید
132
+ - نام‌های جزئی را با نام کامل یکسان‌سازی کنید
133
+ - فقط JSON برگردانید!
134
+
135
+ یک JSON Array برگردانید. هر عنصر دارای:
136
+ - "text": متن دقیق استخراج شده
137
+ - "type": نوع (company, person, amount, percent, phone, email, date, location, id_number)
138
+ - "original": توضیح اضافی (نام کامل اگر جزئی بود، یا پیشوند اگر حذف شده بود)"""
139
+
140
+ def call_cerebras(self, text: str) -> List[Dict]:
141
+ """فراخوانی Cerebras API با پرامپت بهبود شده"""
142
+ logger.info("🔄 فراخوانی Cerebras API با دستورالعمل قوی...")
143
+
144
+ system_prompt = self.get_system_prompt()
145
+ user_prompt = self.get_user_prompt(text)
146
+
147
+ try:
148
+ response = requests.post(
149
+ "https://api.cerebras.ai/v1/chat/completions",
150
+ headers={
151
+ "Authorization": f"Bearer {self.api_key}",
152
+ "Content-Type": "application/json"
153
+ },
154
+ json={
155
+ "model": "llama-3.3-70b",
156
+ "messages": [
157
+ {"role": "system", "content": system_prompt},
158
+ {"role": "user", "content": user_prompt}
159
+ ],
160
+ "max_tokens": 4000,
161
+ "temperature": 0.1
162
+ },
163
+ timeout=30
164
+ )
165
+
166
+ if response.status_code != 200:
167
+ logger.error(f"❌ خطای API Cerebras: {response.text}")
168
+ return []
169
+
170
+ result = response.json()
171
+ content = result['choices'][0]['message']['content']
172
+
173
+ try:
174
+ # تمیز کردن محتوا از markdown اگر وجود داشته باشد
175
+ content = content.replace("```json", "").replace("```", "").strip()
176
+ entities = json.loads(content)
177
+ if not isinstance(entities, list):
178
+ entities = []
179
+ logger.info(f"✅ {len(entities)} موجودیت استخراج شد")
180
+ return entities
181
+ except json.JSONDecodeError:
182
+ logger.error(f"❌ خطا در JSON parsing: {content[:200]}")
183
+ return []
184
+
185
+ except Exception as e:
186
+ logger.error(f"❌ خطا Cerebras: {e}")
187
+ return []
188
+
189
+ def get_placeholder(self, entity_type: str) -> str:
190
+ """تولید placeholder با format جدید"""
191
+ type_lower = entity_type.lower()
192
+ if type_lower not in self.counters:
193
+ type_lower = 'amount'
194
+
195
+ self.counters[type_lower] += 1
196
+ return f"{type_lower}-{self.counters[type_lower]:02d}"
197
+
198
+ def apply_partial_matching(self, entities: List[Dict]) -> List[Dict]:
199
+ """Simple entity matching - Llama handles the complex matching"""
200
+ return entities
201
+
202
+ def anonymize(self, text: str) -> Tuple[str, List]:
203
+ """ناشناس‌سازی متن با قوانین ثبات"""
204
+ logger.info("🚀 شروع ناشناس‌سازی متن...")
205
+
206
+ # تنظیف
207
+ self.mapping_table = {}
208
+ self.seen_entities = {}
209
+ for key in self.counters:
210
+ self.counters[key] = 0
211
+
212
+ # دریافت موجودیت‌ها
213
+ entities = self.call_cerebras(text)
214
+
215
+ if not entities:
216
+ logger.warning("⚠️ موجودیتی شناسایی نشد")
217
+ return text, []
218
+
219
+ logger.info("🔄 Processing entities...")
220
+
221
+ # جایگزینی با قانون ثبات
222
+ anonymized = text
223
+ replacements = []
224
+
225
+ for entity in entities:
226
+ entity_type = entity.get('type', 'amount').lower()
227
+ entity_text = entity.get('text', '').strip()
228
+ original_info = entity.get('original', '')
229
+
230
+ if not entity_text:
231
+ continue
232
+
233
+ # بررسی اگر این موجودیت قبلاً دیده شده است
234
+ entity_key = (entity_type, entity_text.lower())
235
+
236
+ # اگر این نام جزئی است (دارای original)، برای یکسان‌سازی
237
+ # نام کامل را نیز بررسی کنید
238
+ token = None
239
+ if original_info:
240
+ # اول چک کنید نام کامل دیده شده یا نه
241
+ full_key = (entity_type, original_info.lower())
242
+ if full_key in self.seen_entities:
243
+ token = self.seen_entities[full_key]
244
+ logger.info(f"🔄 نام جزئی: {entity_text} ← {original_info} → {token}")
245
+ else:
246
+ # نام کامل هنوز دیده نشده، پس نام جزئی را ذخیره کنید
247
+ if entity_key in self.seen_entities:
248
+ token = self.seen_entities[entity_key]
249
+ else:
250
+ token = self.get_placeholder(entity_type)
251
+ self.seen_entities[entity_key] = token
252
+ # نام کامل را نیز ذخیره کنید
253
+ self.seen_entities[full_key] = token
254
+ logger.info(f"✅ نام کامل: {entity_text} → {token}")
255
+ else:
256
+ # نام کامل یا تک‌تک نام
257
+ if entity_key in self.seen_entities:
258
+ token = self.seen_entities[entity_key]
259
+ logger.info(f"🔄 موجودیت تکراری: {entity_text} → {token}")
260
+ else:
261
+ token = self.get_placeholder(entity_type)
262
+ self.seen_entities[entity_key] = token
263
+ logger.info(f"✅ جایگزینی: {entity_text} → {token}")
264
+
265
+ if not token:
266
+ continue
267
+
268
+ # جدول نگاشت را تنظیم کنید
269
+ if token not in self.mapping_table:
270
+ self.mapping_table[token] = {
271
+ 'original': original_info or entity_text,
272
+ 'type': entity_type,
273
+ 'note': ''
274
+ }
275
+
276
+ # جایگزینی دقیق (case-sensitive اول، سپس case-insensitive)
277
+ idx = anonymized.find(entity_text)
278
+ if idx != -1:
279
+ anonymized = anonymized[:idx] + token + anonymized[idx + len(entity_text):]
280
+ replacements.append({
281
+ 'original': entity_text,
282
+ 'placeholder': token,
283
+ 'type': entity_type
284
+ })
285
+
286
+ logger.info(f"✅ ناشناس‌سازی کامل - {len(self.mapping_table)} نگاشت")
287
+ return anonymized, entities
288
+
289
+ def get_mapping_table_str(self) -> str:
290
+ """جدول نگاشت جزئی"""
291
+ if not self.mapping_table:
292
+ return "❌ موجودیتی شناسایی نشد"
293
+
294
+ result = "## 📊 جدول نگاشت\n\n"
295
+ result += "| توکن | اطلاعات اصلی | نوع |\n"
296
+ result += "|------|--------|------|\n"
297
+
298
+ for token, info in sorted(self.mapping_table.items()):
299
+ entity_type = info.get('type', 'unknown')
300
+ original = info.get('original', '')
301
+ note = info.get('note', '')
302
+
303
+ note_str = f" ({note})" if note else ""
304
+ result += f"| `{token}` | {original}{note_str} | {entity_type} |\n"
305
+
306
+ return result
307
+
308
+ def restore(self, text: str) -> str:
309
+ """بازگردانی اطلاعات اصلی"""
310
+ logger.info("🔄 بازگردانی اطلاعات...")
311
+ restored = text
312
+ for token, info in self.mapping_table.items():
313
+ original = info.get('original', '')
314
+ restored = restored.replace(token, original)
315
+ logger.info("✅ بازگردانی کامل")
316
+ return restored
317
+
318
+
319
+ # متغیرهای global
320
+ anonymizer = None
321
+
322
+ def process(input_text: str) -> Tuple[str, str, str, str, str]:
323
+ """
324
+ روند کامل:
325
+ 1. نا��ناس‌سازی با Cerebras (llama-3.3-70b) + پرامپت قوی
326
+ 2. ارسال به ChatGPT (حتما!)
327
+ 3. بازگردانی پاسخ ChatGPT
328
+ """
329
+ global anonymizer
330
+
331
+ try:
332
+ if not input_text.strip():
333
+ return "", "", "", "", ""
334
+
335
+ # دریافت API Keys
336
+ api_key_cerebras = os.getenv("CEREBRAS_API_KEY")
337
+ api_key_gpt = os.getenv("OPENAI_API_KEY")
338
+
339
+ if not api_key_gpt:
340
+ logger.error("❌ OPENAI_API_KEY یافت نشد")
341
+ return "", "", "", "", ""
342
+
343
+ if not api_key_cerebras:
344
+ logger.error("❌ CEREBRAS_API_KEY یافت نشد")
345
+ return "", "", "", "", ""
346
+
347
+ # ============================================
348
+ # مرحله 1: مقداردهی
349
+ # ============================================
350
+ if not anonymizer:
351
+ logger.info("Initializing anonymizer...")
352
+ anonymizer = AnonymizerCerebrasEnhanced()
353
+
354
+ # ============================================
355
+ # مرحله 2: ناشناس‌سازی با پرامپت قوی
356
+ # ============================================
357
+ logger.info("Step 1: Anonymizing text with Cerebras...")
358
+
359
+ anonymized_text, entities = anonymizer.anonymize(input_text)
360
+
361
+ if not entities:
362
+ logger.warning("⚠️ موجودیتی شناسایی نشد - متن ناشناس نشد")
363
+ return input_text, "", "", "", ""
364
+
365
+ # ============================================
366
+ # مرحله 3: جدول نگاشت
367
+ # ============================================
368
+ logger.info("Step 2: Creating mapping table")
369
+ mapping = anonymizer.get_mapping_table_str()
370
+ logger.info(f"📋 {len(anonymizer.mapping_table)} نگاشت ایجاد شد")
371
+
372
+ # ============================================
373
+ # مرحله 4: ارسال به ChatGPT (حتما!)
374
+ # ============================================
375
+ logger.info("Step 3: Sending to ChatGPT...")
376
+
377
+ prompt = f"""متن ناشناس‌شده زیر (متن مالی) را تحلیل و خلاصه کنید.
378
+
379
+ متن:
380
+ {anonymized_text}
381
+
382
+ لطفاً:
383
+ 1. خلاصه‌ای مختصر و معنادار ارائه دهید
384
+ 2. نکات اصلی را مشخص کنید
385
+ 3. تمام توکن‌های ناشناس (مثل company-01، amount-02) را حفظ کنید
386
+ 4. تنها اطلاعات موجود در متن را بیان کنید"""
387
+
388
+ logger.info(f"📤 ارسال به ChatGPT (gpt-4o-mini)...")
389
+
390
+ try:
391
+ gpt_response_obj = requests.post(
392
+ "https://api.openai.com/v1/chat/completions",
393
+ headers={"Authorization": f"Bearer {api_key_gpt}"},
394
+ json={
395
+ "model": "gpt-4o-mini",
396
+ "messages": [
397
+ {
398
+ "role": "system",
399
+ "content": "شما دستیار تحلیل متون مالی فارسی هستید. متن‌های ناشناس‌شده را دقیق تحلیل کنید. تمام توکن‌های ناشناس را حفظ کنید."
400
+ },
401
+ {"role": "user", "content": prompt}
402
+ ],
403
+ "max_tokens": 1500,
404
+ "temperature": 0.7
405
+ },
406
+ timeout=30
407
+ )
408
+
409
+ if gpt_response_obj.status_code == 200:
410
+ gpt_response = gpt_response_obj.json()['choices'][0]['message']['content']
411
+ logger.info("✅ پاسخ دریافت شد")
412
+ else:
413
+ error_text = gpt_response_obj.json().get('error', {}).get('message', gpt_response_obj.text)
414
+ logger.error(f"❌ خطای ChatGPT: {error_text}")
415
+ return input_text, anonymized_text, "", "", mapping
416
+
417
+ except Exception as e:
418
+ logger.error(f"❌ خطا در ارسال به ChatGPT: {e}")
419
+ return input_text, anonymized_text, "", "", mapping
420
+
421
+ # ============================================
422
+ # مرحله 5: بازگردانی پاسخ ChatGPT
423
+ # ============================================
424
+ logger.info("Step 4: Restoring original text...")
425
+
426
+ restored_text = anonymizer.restore(gpt_response)
427
+
428
+ logger.info(f"✅ بازگردانی کامل")
429
+
430
+ logger.info(f"Done. Input: {len(input_text)} | Anonymized: {len(anonymized_text)} | Entities: {len(entities)}")
431
+
432
+ return input_text, anonymized_text, gpt_response, restored_text, mapping
433
+
434
+ except Exception as e:
435
+ logger.error(f"❌ خطا عمومی: {e}", exc_info=True)
436
+ return "", "", "", "", ""
437
+
438
+ def clear():
439
+ """پاک کردن"""
440
+ return "", "", "", "", ""
441
+
442
+ # رابط Gradio
443
+ with gr.Blocks(title="Text Anonymization", theme=gr.themes.Soft()) as app:
444
+
445
+ with gr.Row():
446
+ with gr.Column(scale=2):
447
+ input_text = gr.Textbox(
448
+ lines=12,
449
+ placeholder="متن را وارد کنید...",
450
+ label="Input"
451
+ )
452
+
453
+ with gr.Column(scale=1):
454
+ process_btn = gr.Button("Process", variant="primary", size="lg")
455
+ clear_btn = gr.Button("Clear", variant="stop")
456
+
457
+ with gr.Row():
458
+ with gr.Column():
459
+ anonymized_text = gr.Textbox(
460
+ lines=10,
461
+ label="Anonymized",
462
+ interactive=False
463
+ )
464
+
465
+ with gr.Column():
466
+ gpt_response = gr.Textbox(
467
+ lines=10,
468
+ label="GPT Response",
469
+ interactive=False
470
+ )
471
+
472
+ with gr.Column():
473
+ restored_text = gr.Textbox(
474
+ lines=10,
475
+ label="Restored",
476
+ interactive=False
477
+ )
478
+
479
+ with gr.Row():
480
+ with gr.Column():
481
+ mapping = gr.Textbox(
482
+ lines=10,
483
+ label="Mapping",
484
+ interactive=False
485
+ )
486
+
487
+ # Event handlers
488
+ process_btn.click(
489
+ fn=process,
490
+ inputs=[input_text],
491
+ outputs=[input_text, anonymized_text, gpt_response, restored_text, mapping]
492
+ )
493
+
494
+ clear_btn.click(
495
+ fn=clear,
496
+ outputs=[input_text, anonymized_text, gpt_response, restored_text, mapping]
497
+ )
498
+
499
+ if __name__ == "__main__":
500
+ print("Starting Text Anonymization System...")
501
+ app.launch(
502
+ server_name="0.0.0.0",
503
+ server_port=7860,
504
+ share=False,
505
+ show_error=True
506
+ )