leilaghomashchi commited on
Commit
df649d8
·
verified ·
1 Parent(s): a97a75e

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -576
app.py DELETED
@@ -1,576 +0,0 @@
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
- """ایجاد دستورالعمل سیستمی پیشرفته برای Cerebras Llama 3.3-70B"""
30
- return """شما یک «ناشناس‌ساز متون مالی فارسی پیشرفته» هستید. وظیفه‌تان:
31
- 1. استخراج دقیق تمام موجودیت‌های حساس
32
- 2. Entity Linking: ربط دادن نام‌های مختلف به یک موجودیت
33
- 3. نگاشت ثابت تمام موجودیت‌ها
34
- 4. ناشناس‌سازی دقیق و کامل
35
-
36
- ## **📋 قوانین CRITICAL برای Entity Linking**
37
-
38
- ### **1. ENTITY LINKING برای PERSONS (اشخاص)**
39
-
40
- #### **قانون اول: نام کامل = معرف اصلی**
41
- اگر "سروش خسروی" = person-01 شود:
42
- - "سروش خسروی" → person-01 ✅
43
- - "خسروی" تنهایی → person-01 ✅ (نام خانوادگی)
44
- - "سروش" تنهایی → person-01 ✅ (نام کوچک)
45
- - "او" / "وی" / "ایشان" → person-01 ✅ (ضمیر)
46
- - "مهندس خسروی" → person-01 ✅ (عنوان + نام خانوادگی)
47
-
48
- #### **قانون دوم: هیچ نام خانوادگی/کوچک تنهایی را رها نکن**
49
- ✅ صحیح: "سروش خسروی در برنامه. خسروی گفت..." → هر دو را person-01
50
- ❌ غلط: فقط خسروی را ناشناس کردن
51
-
52
- #### **قانون سوم: الگوهای شامل نام**
53
- اگر "سروش خسروی" = person-01، همه اینها = person-01:
54
- - "سروش خسروی"
55
- - "خسروی"
56
- - "سروش"
57
- - "مهندس خسروی"
58
- - "رئیس خسروی"
59
- - "وی" / "او" / "ایشان"
60
-
61
- #### **قانون چهارم: ترتیب اولویت**
62
- 1. نام کامل: "سروش خسروی" → person-01
63
- 2. نام خانوادگی: "خسروی" → person-01
64
- 3. نام کوچک: "سروش" → person-01
65
- 4. عنوان + نام: "مهندس خسروی" → person-01
66
- 5. ضمیرهای اشاره: "وی" → person-01
67
-
68
- ### **2. ENTITY LINKING برای COMPANIES (شرکت‌ها)**
69
-
70
- #### **قانون اول: نام کامل = معرف اصلی**
71
- اگر "پتروشیمی غدیر (ماهشهر)" = company-01 شود:
72
- - "پتروشیمی غدیر (ماهشهر)" → company-01 ✅
73
- - "پتروشیمی غدیر" → company-01 ✅
74
- - "غدیر" → company-01 ✅
75
- - "این شرکت" (در فقره بعد) → company-01 ✅
76
- - "شرکت" (در زمینه روشن) → company-01 ✅
77
-
78
- #### **قانون دوم: نام‌های اختصاری**
79
- اگر "نماد سبهان" = company-02:
80
- - "نماد سبهان" → نماد company-02 ✅
81
- - "سبهان" → company-02 ✅
82
- - "نماد" تنهایی (در متن روشن) → company-02 ✅
83
-
84
- ### **3. ENTITY LINKING برای AMOUNTS (مقادیر مالی)**
85
-
86
- #### **قانون اول: واحد یکسان = یک موجودیت**
87
- ✅ صحیح: "142 میلیارد تومان" → amount-01 | "142" دوباره → amount-01
88
- ❌ غلط: "142" دوباره → amount-02 (NEW - غلط!)
89
-
90
- #### **قانون دوم: واحد متفاوت = موجودیت مختلف**
91
- "142 میلیارد تومان" → amount-01
92
- "153,194 تن" → amount-02 (واحد متفاوت است)
93
-
94
- #### **قانون سوم: فرمت‌های مختلف عدد یکسان**
95
- ✅ صحیح: "142 میلیارد" → amount-01 | "۱۴۲ میلیارد" → amount-01
96
- ❌ غلط: دو موجودیت متفاوت برای یک عدد
97
-
98
- ### **4. ENTITY LINKING برای PERCENTS (درصدها)**
99
-
100
- #### **قانون اول: مقدار و واحد یکسان**
101
- "21 درصد" → percent-01
102
- "21%" یا "۲۱ درصدی" → percent-01 ✅
103
- "45 درصد" → percent-02 ✅ (متفاوت است)
104
-
105
- #### **قانون دوم: درصد در سیاق‌های مختلف**
106
- "رشد 21 درصدی" → percent-01
107
- "افزایش 21 درصد" → percent-01 ✅ (همان درصد)
108
-
109
- ## **🔍 الگوریتم STEP-BY-STEP**
110
-
111
- ### **مرحله 1: استخراج نخستین**
112
- تمام موجودیت‌های کامل را بدون linked کردن استخراج کن
113
-
114
- ### **مرحله 2: Entity Linking**
115
- برای هر موجودیت تام:
116
- - نام خانوادگی استخراج کن (اگر موجود باشد)
117
- - نام کوچک استخراج کن (اگر موجود باشد)
118
- - در تمام متن جستجو کن
119
- - تمام تکرارها = یک موجودیت
120
-
121
- ### **مرحله 3: نگاشت نهایی**
122
- برای هر موجودیت اصلی، تمام linked forms را ثبت کن
123
-
124
- ### **مرحله 4: ناشناس‌سازی**
125
- جایگزین کن:
126
- 1. نام‌های کامل اول
127
- 2. سپس نام‌های جزئی
128
- 3. سپس ضمیرهایی که الحاق شده‌اند
129
-
130
- ## **✅ CRITICAL DO's:**
131
- ✅ "خسروی" بعد از "سروش خسروی" → person-01
132
- ✅ "غدیر" بعد از "پتروشیمی غدیر" → company-01
133
- ✅ "این شرکت" در فقره بعد از ذکر شرکت → company-01
134
- ✅ "وی" که اشاره به شخص → person-01
135
- ✅ تمام تکرار نام خانوادگی → همان person
136
- ✅ نام کامل مختلف → person متفاوت
137
-
138
- ## **❌ CRITICAL DON'Ts:**
139
- ❌ نام خانوادگی تنهایی را رها کردن
140
- ❌ نام کوچک تنهایی را رها کردن
141
- ❌ ضمیرهای مرتبط را رها کردن
142
- ❌ فرض کردن اینکه "خسروی" مختلف است
143
- ❌ نادیده گرفتن زمینه برای "این شرکت"
144
- ❌ دو بار شماره‌گذاری موجودیت یکسان
145
-
146
- ## **⚠️ قوانین اندیس‌گذاری:**
147
- - شرکت‌ها: company-01, company-02, company-03, ... (پیوسته و بدون گپ)
148
- - اشخاص: person-01, person-02, person-03, ... (پیوسته و بدون گپ)
149
- - مقادیر: amount-01, amount-02, amount-03, ... (پیوسته و بدون گپ)
150
- - درصدها: percent-01, percent-02, percent-03, ... (پیوسته و بدون گپ)
151
- - تاریخها: date-01, date-02, date-03, ... (پیوسته و بدون گپ)
152
-
153
- ## **موارد حفظ شده:**
154
- - عناوین شغلی: مدیرعامل، رئیس کل، مدیرکل، سرپرست
155
- - واحدها: میلیارد تومان، هزار ریال، دلار، تن، دستگاه
156
- - کلمات توضیحی: "شرکت"، "بانک"، "گروه"، "سازمان"
157
-
158
- ## **ممنوع:**
159
- ❌ کلمات انگلیسی اضافی
160
- ❌ تغییر ساختار جمله
161
- ❌ حذف یا اضافه کردن کلمات
162
- ❌ نام خانوادگی یا نام کوچک تنهایی را بدون linking رها کردن
163
-
164
- **فقط متن ناشناس‌شده را برگردان - هیچ توضیح اضافی نیاز نیست."""
165
-
166
- def get_user_prompt(self, text: str) -> str:
167
- """تشکیل پرامپت کاربر قوی‌تر برای Entity Linking دقیق"""
168
- return f"""متن مالی فارسی زیر را با دقت تحلیل کنید و تمام موجودیت‌های حساس را ناشناس کنید.
169
-
170
- متن:
171
- {text}
172
-
173
- **دستورات CRITICAL برای Entity Linking:**
174
-
175
- ### **برای اشخاص (PERSONS):**
176
- 1️⃣ اگر "سروش خسروی" = person-01 شد:
177
- - نام خانوادگی تنهایی "خسروی" → person-01
178
- - نام کوچک تنهایی "سروش" → person-01
179
- - ضمیرهای "وی"، "او"، "ایشان" → person-01
180
- - "مهندس خسروی" یا هر ترکیب → person-01
181
-
182
- 2️⃣ هیچ نام خانوادگی یا نام کوچک تنهایی را رها نکن
183
-
184
- ### **برای شرکت‌ها (COMPANIES):**
185
- 1️⃣ اگر "پتروشیمی غدیر" = company-01 شد:
186
- - نام کوتاه "غدیر" → company-01
187
- - "این شرکت" در فقره بعد → company-01
188
- - "شرکت" در زمینه روشن → company-01
189
-
190
- 2️⃣ تمام نام‌های مختلف برای یک شرکت را linked کن
191
-
192
- ### **برای مقادیر (AMOUNTS):**
193
- 1️⃣ اگر "142 میلیارد تومان" = amount-01:
194
- - "142 میلیارد" یا صرفاً "142" = amount-01
195
- - "۱۴۲ میلیارد" = amount-01 (فرمت متفاوت، عدد یکسان)
196
-
197
- 2️⃣ واحد متفاوت = موجودیت متفاوت
198
-
199
- ### **برای درصدها (PERCENTS):**
200
- 1️⃣ اگر "21 درصد" = percent-01:
201
- - "21%" = percent-01
202
- - "۲۱ درصدی" = percent-01
203
- - "رشد 21 درصدی" = percent-01
204
-
205
- **‌خروجی:**
206
- ناشناس‌سازی متن با رعایت تمام قوانین Entity Linking فوق.
207
-
208
- **مهم:**
209
- ✅ تمام نام‌های جزئی (نام خانوادگی، نام کوچک) linked باشند
210
- ✅ تمام ضمیرهای مرتبط linked باشند
211
- ✅ تمام نام‌های مختلف یک شرکت linked باشند
212
- ✅ مقادیر و درصدهای یکسان (با واحد/فرمت یکسان) = یک موجودیت
213
-
214
- ❌ هیچ نام خانوادگی تنهایی رها نشود
215
- ❌ هیچ ضمیر مرتبط رها نشود
216
- ❌ هیچ نام اختصاری شرکت رها نشود
217
-
218
- **فقط متن ناشناس‌شده را برگردان - توضیح اضافی نده!**"""
219
-
220
- def call_cerebras(self, text: str) -> List[Dict]:
221
- """فراخوانی Cerebras API با پرامپت بهبود شده"""
222
- logger.info("🔄 فراخوانی Cerebras API با دستورالعمل قوی...")
223
-
224
- system_prompt = self.get_system_prompt()
225
- user_prompt = self.get_user_prompt(text)
226
-
227
- try:
228
- response = requests.post(
229
- "https://api.cerebras.ai/v1/chat/completions",
230
- headers={
231
- "Authorization": f"Bearer {self.api_key}",
232
- "Content-Type": "application/json"
233
- },
234
- json={
235
- "model": "llama-3.3-70b",
236
- "messages": [
237
- {"role": "system", "content": system_prompt},
238
- {"role": "user", "content": user_prompt}
239
- ],
240
- "max_tokens": 4000,
241
- "temperature": 0.1
242
- },
243
- timeout=30
244
- )
245
-
246
- if response.status_code != 200:
247
- logger.error(f"❌ خطای API Cerebras: {response.text}")
248
- return []
249
-
250
- result = response.json()
251
- content = result['choices'][0]['message']['content']
252
-
253
- try:
254
- # تمیز کردن محتوا از markdown اگر وجود داشته باشد
255
- content = content.replace("```json", "").replace("```", "").strip()
256
- entities = json.loads(content)
257
- if not isinstance(entities, list):
258
- entities = []
259
- logger.info(f"✅ {len(entities)} موجودیت استخراج شد")
260
- return entities
261
- except json.JSONDecodeError:
262
- logger.error(f"❌ خطا در JSON parsing: {content[:200]}")
263
- return []
264
-
265
- except Exception as e:
266
- logger.error(f"❌ خطا Cerebras: {e}")
267
- return []
268
-
269
- def get_placeholder(self, entity_type: str) -> str:
270
- """تولید placeholder با format جدید"""
271
- type_lower = entity_type.lower()
272
- if type_lower not in self.counters:
273
- type_lower = 'amount'
274
-
275
- self.counters[type_lower] += 1
276
- return f"{type_lower}-{self.counters[type_lower]:02d}"
277
-
278
- def anonymize(self, text: str) -> Tuple[str, List]:
279
- """ناشناس‌سازی متن با قوانین ثبات"""
280
- logger.info("🚀 شروع ناشناس‌سازی متن...")
281
-
282
- # تنظیف
283
- self.mapping_table = {}
284
- self.seen_entities = {}
285
- for key in self.counters:
286
- self.counters[key] = 0
287
-
288
- # دریافت موجودیت‌ها
289
- entities = self.call_cerebras(text)
290
-
291
- if not entities:
292
- logger.warning("⚠️ موجودیتی شناسایی نشد")
293
- return text, []
294
-
295
- logger.info("🔄 Processing entities...")
296
-
297
- # جایگزینی با قانون ثبات
298
- anonymized = text
299
- replacements = []
300
-
301
- for entity in entities:
302
- entity_type = entity.get('type', 'amount').lower()
303
- entity_text = entity.get('text', '').strip()
304
- original_info = entity.get('original', '')
305
-
306
- if not entity_text:
307
- continue
308
-
309
- # بررسی اگر این موجودیت قبلاً دیده شده است
310
- entity_key = (entity_type, entity_text.lower())
311
-
312
- if entity_key in self.seen_entities:
313
- token = self.seen_entities[entity_key]
314
- logger.info(f"🔄 موجودیت تکراری: {entity_text} → {token}")
315
- else:
316
- token = self.get_placeholder(entity_type)
317
- self.seen_entities[entity_key] = token
318
- self.mapping_table[token] = {
319
- 'original': entity_text,
320
- 'type': entity_type,
321
- 'note': original_info
322
- }
323
- logger.info(f"✅ جایگزینی: {entity_text} → {token}")
324
-
325
- # جایگزینی دقیق (case-sensitive اول، سپس case-insensitive)
326
- idx = anonymized.find(entity_text)
327
- if idx != -1:
328
- anonymized = anonymized[:idx] + token + anonymized[idx + len(entity_text):]
329
- replacements.append({
330
- 'original': entity_text,
331
- 'placeholder': token,
332
- 'type': entity_type
333
- })
334
-
335
- logger.info(f"✅ ناشناس‌سازی کامل - {len(self.mapping_table)} نگاشت")
336
- return anonymized, entities
337
-
338
- def get_mapping_table_str(self) -> str:
339
- """جدول نگاشت جزئی"""
340
- if not self.mapping_table:
341
- return "❌ موجودیتی شناسایی نشد"
342
-
343
- result = "## 📊 جدول نگاشت\n\n"
344
- result += "| توکن | اطلاعات اصلی | نوع |\n"
345
- result += "|------|--------|------|\n"
346
-
347
- for token, info in sorted(self.mapping_table.items()):
348
- entity_type = info.get('type', 'unknown')
349
- original = info.get('original', '')
350
- note = info.get('note', '')
351
-
352
- note_str = f" ({note})" if note else ""
353
- result += f"| `{token}` | {original}{note_str} | {entity_type} |\n"
354
-
355
- return result
356
-
357
- def restore(self, text: str) -> str:
358
- """بازگردانی اطلاعات اصلی"""
359
- logger.info("🔄 بازگردانی اطلاعات...")
360
- restored = text
361
- for token, info in self.mapping_table.items():
362
- original = info.get('original', '')
363
- restored = restored.replace(token, original)
364
- logger.info("✅ بازگردانی کامل")
365
- return restored
366
-
367
-
368
- # متغیرهای global
369
- anonymizer = None
370
-
371
- def process(input_text: str) -> Tuple[str, str, str, str, str]:
372
- """
373
- روند کامل:
374
- 1. ناشناس‌سازی با Cerebras (llama-3.3-70b) + پرامپت قوی
375
- 2. ارسال به ChatGPT (حتما!)
376
- 3. بازگردانی پاسخ ChatGPT
377
- """
378
- global anonymizer
379
-
380
- try:
381
- if not input_text.strip():
382
- return "", "", "", "", ""
383
-
384
- # دریافت API Keys
385
- api_key_cerebras = os.getenv("CEREBRAS_API_KEY")
386
- api_key_gpt = os.getenv("OPENAI_API_KEY")
387
-
388
- if not api_key_gpt:
389
- logger.error("❌ OPENAI_API_KEY یافت نشد")
390
- return "", "", "", "", ""
391
-
392
- if not api_key_cerebras:
393
- logger.error("❌ CEREBRAS_API_KEY یافت نشد")
394
- return "", "", "", "", ""
395
-
396
- # ============================================
397
- # مرحله 1: مقداردهی
398
- # ============================================
399
- if not anonymizer:
400
- logger.info("Initializing anonymizer...")
401
- anonymizer = AnonymizerCerebrasEnhanced()
402
-
403
- # ============================================
404
- # مرحله 2: ناشناس‌سازی با پرامپت قوی
405
- # ============================================
406
- logger.info("Step 1: Anonymizing text with Cerebras...")
407
-
408
- anonymized_text, entities = anonymizer.anonymize(input_text)
409
-
410
- if not entities:
411
- logger.warning("⚠️ موجودیتی شناسایی نشد - متن ناشناس نشد")
412
- return input_text, "", "", "", ""
413
-
414
- # ============================================
415
- # مرحله 3: جدول نگاشت
416
- # ============================================
417
- logger.info("Step 2: Creating mapping table")
418
- mapping = anonymizer.get_mapping_table_str()
419
- logger.info(f"📋 {len(anonymizer.mapping_table)} نگاشت ایجاد شد")
420
-
421
- # ============================================
422
- # مرحله 4: ارسال به ChatGPT (حتما!)
423
- # ============================================
424
- logger.info("Step 3: Sending to ChatGPT...")
425
-
426
- prompt = f"""متن ناشناس‌شده زیر (متن مالی) را تحلیل و خلاصه کنید.
427
-
428
- متن:
429
- {anonymized_text}
430
-
431
- لطفاً:
432
- 1. خلاصه‌ای مختصر و معنادار ارائه دهید
433
- 2. نکات اصلی را مشخص کنید
434
- 3. تمام توکن‌های ناشناس (مثل company-01، amount-02) را حفظ کنید
435
- 4. تنها اطلاعات موجود در متن را بیان کنید"""
436
-
437
- logger.info(f"📤 ارسال به ChatGPT (gpt-4o-mini)...")
438
-
439
- try:
440
- gpt_response_obj = requests.post(
441
- "https://api.openai.com/v1/chat/completions",
442
- headers={"Authorization": f"Bearer {api_key_gpt}"},
443
- json={
444
- "model": "gpt-4o-mini",
445
- "messages": [
446
- {
447
- "role": "system",
448
- "content": "شما دستیار تحلیل متون مالی فارسی هستید. متن‌های ناشناس‌شده را دقیق تحلیل کنید. تمام توکن‌های ناشناس را حفظ کنید."
449
- },
450
- {"role": "user", "content": prompt}
451
- ],
452
- "max_tokens": 1500,
453
- "temperature": 0.7
454
- },
455
- timeout=30
456
- )
457
-
458
- if gpt_response_obj.status_code == 200:
459
- gpt_response = gpt_response_obj.json()['choices'][0]['message']['content']
460
- logger.info("✅ پاسخ دریافت شد")
461
- else:
462
- error_text = gpt_response_obj.json().get('error', {}).get('message', gpt_response_obj.text)
463
- logger.error(f"❌ خطای ChatGPT: {error_text}")
464
- return input_text, anonymized_text, "", "", mapping
465
-
466
- except Exception as e:
467
- logger.error(f"❌ خطا در ارسال به ChatGPT: {e}")
468
- return input_text, anonymized_text, "", "", mapping
469
-
470
- # ============================================
471
- # مرحله 5: بازگردانی پاسخ ChatGPT
472
- # ============================================
473
- logger.info("Step 4: Restoring original text...")
474
-
475
- restored_text = anonymizer.restore(gpt_response)
476
-
477
- logger.info(f"✅ بازگردانی کامل")
478
-
479
- logger.info(f"Done. Input: {len(input_text)} | Anonymized: {len(anonymized_text)} | Entities: {len(entities)}")
480
-
481
- return input_text, anonymized_text, gpt_response, restored_text, mapping
482
-
483
- except Exception as e:
484
- logger.error(f"❌ خطا عمومی: {e}", exc_info=True)
485
- return "", "", "", "", ""
486
-
487
- def clear():
488
- """پاک کردن"""
489
- empty_mapping = "### 📋 جدول نگاشت\nدر انتظار پردازش..."
490
- return "", "", "", "", empty_mapping
491
-
492
- # رابط Gradio - کاملاً فارسی‌زبان و RTL
493
- css_rtl = """
494
- #input_text textarea { direction: rtl; text-align: right; }
495
- #anonymized_text textarea { direction: rtl; text-align: right; }
496
- #gpt_response textarea { direction: rtl; text-align: right; }
497
- #restored_text textarea { direction: rtl; text-align: right; }
498
- """
499
-
500
- with gr.Blocks(title="سیستم ناشناس‌سازی متون", theme=gr.themes.Soft(), css=css_rtl) as app:
501
-
502
- gr.Markdown("# 🔐 سیستم ناشناس‌سازی متون مالی فارسی")
503
- gr.Markdown("#### استخراج موجودیت‌های حساس و ناشناس‌سازی آنها")
504
-
505
- with gr.Row():
506
- # بلوک 1: متن ورودی (سمت راست)
507
- with gr.Column(scale=2):
508
- input_text = gr.Textbox(
509
- lines=12,
510
- placeholder="متن مالی/خبری را وارد کنید...",
511
- label="📝 متن ورودی",
512
- elem_id="input_text"
513
- )
514
-
515
- # دکمه‌های کنترل
516
- with gr.Column(scale=1):
517
- gr.HTML("<div style='text-align: center; margin-bottom: 10px;'></div>")
518
- process_btn = gr.Button("🔄 پردازش", variant="primary", size="lg")
519
- clear_btn = gr.Button("🗑️ پاک کردن", variant="stop", size="lg")
520
-
521
- # بلوک 2: متن ناشناس‌سازی شده
522
- with gr.Row():
523
- with gr.Column(scale=1):
524
- anonymized_text = gr.Textbox(
525
- lines=10,
526
- label="🔒 متن ناشناس‌شده",
527
- interactive=False,
528
- elem_id="anonymized_text"
529
- )
530
-
531
- # بلوک 3: پاسخ ChatGPT
532
- with gr.Column(scale=1):
533
- gpt_response = gr.Textbox(
534
- lines=10,
535
- label="🤖 تحلیل ChatGPT",
536
- interactive=False,
537
- elem_id="gpt_response"
538
- )
539
-
540
- # بلوک 4: متن بازگردانی شده (سمت چپ)
541
- with gr.Column(scale=1):
542
- restored_text = gr.Textbox(
543
- lines=10,
544
- label="✅ متن بازگردانی شده",
545
- interactive=False,
546
- elem_id="restored_text"
547
- )
548
-
549
- # بلوک 5: جدول نگاشت به صورت مارکداون
550
- with gr.Row():
551
- with gr.Column():
552
- mapping = gr.Markdown(
553
- value="### 📋 جدول نگاشت\nدر انتظار پردازش...",
554
- label="📋 جدول نگاشت"
555
- )
556
-
557
- # Event handlers
558
- process_btn.click(
559
- fn=process,
560
- inputs=[input_text],
561
- outputs=[input_text, anonymized_text, gpt_response, restored_text, mapping]
562
- )
563
-
564
- clear_btn.click(
565
- fn=clear,
566
- outputs=[input_text, anonymized_text, gpt_response, restored_text, mapping]
567
- )
568
-
569
- if __name__ == "__main__":
570
- print("🚀 سیستم ناشناس‌سازی متون در حال راه‌اندازی...")
571
- app.launch(
572
- server_name="0.0.0.0",
573
- server_port=7860,
574
- share=False,
575
- show_error=True
576
- )