leilaghomashchi commited on
Commit
2d8181d
·
verified ·
1 Parent(s): d2a88af

Delete app.py

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