leilaghomashchi commited on
Commit
7b1c948
·
verified ·
1 Parent(s): 8026ab1

Upload app_rtl_fa_final.py

Browse files
Files changed (1) hide show
  1. app_rtl_fa_final.py +459 -0
app_rtl_fa_final.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import re
3
+ import os
4
+ import requests
5
+ import logging
6
+ from typing import Dict, List, Tuple
7
+
8
+ logging.basicConfig(level=logging.INFO)
9
+ logger = logging.getLogger(__name__)
10
+
11
+ class AnonymizerAdvanced:
12
+ """ناشناس‌ساز پیشرفته با روش‌های متعدد"""
13
+
14
+ def __init__(self, cerebras_key: str = None, gpt_key: str = None):
15
+ self.cerebras_key = cerebras_key or os.getenv("CEREBRAS_API_KEY")
16
+ self.gpt_key = gpt_key or os.getenv("OPENAI_API_KEY")
17
+ self.mapping_table = {} # {placeholder: original_text}
18
+ self.reverse_mapping = {} # {original_text: placeholder}
19
+
20
+ logger.info("✅ Anonymizer Advanced مقداردهی شد")
21
+
22
+ def anonymize_with_regex(self, text: str) -> Tuple[str, Dict]:
23
+ """ناشناس‌سازی با Regex"""
24
+ logger.info("🔍 روش Regex...")
25
+
26
+ anonymized = text
27
+ mapping = {}
28
+ counters = {'person': 0, 'company': 0, 'amount': 0, 'percent': 0}
29
+
30
+ patterns = [
31
+ ('percent', r'\d+(?:\.\d+)?\s*(?:درصد|%|درصدی)'),
32
+ ('amount', r'\d+(?:\.\d+)?\s*(?:میلیارد|میلیون|هزار|تومان|ریال|دلار|تن|دستگاه|یورو)'),
33
+ ('company', r'(?:شرکت|بانک|سازمان|گروه|هلدینگ|گمرک|دپارتمان|شعبه|اداره)\s+[ء-ي]+(?:\s+[ء-ي]+)*'),
34
+ ('person', r'\b[ء-ي]+\s+[ء-ي]+(?:\s+[ء-ي]+)*\b'),
35
+ ]
36
+
37
+ for entity_type, pattern in patterns:
38
+ for match in re.finditer(pattern, anonymized):
39
+ text_match = match.group()
40
+ key = text_match.lower().strip()
41
+
42
+ if key not in mapping:
43
+ counters[entity_type] += 1
44
+ placeholder = f"{entity_type}-{counters[entity_type]:02d}"
45
+ mapping[key] = {'placeholder': placeholder, 'type': entity_type}
46
+ self.mapping_table[placeholder] = text_match
47
+ self.reverse_mapping[text_match] = placeholder
48
+
49
+ # جایگزینی متن
50
+ for original, info in mapping.items():
51
+ anonymized = anonymized.replace(original, info['placeholder'])
52
+
53
+ logger.info(f"✅ Regex: {sum(counters.values())} موجودیت")
54
+ return anonymized, self.mapping_table
55
+
56
+ def anonymize_with_cerebras(self, text: str) -> Tuple[str, Dict]:
57
+ """ناشناس‌سازی با Cerebras"""
58
+ logger.info("🧠 روش Cerebras...")
59
+
60
+ if not self.cerebras_key:
61
+ logger.warning("⚠️ Cerebras API Key نیست - استفاده از Regex")
62
+ return self.anonymize_with_regex(text)
63
+
64
+ try:
65
+ prompt = f"""متن زیر را ناشناس کنید. قوانین:
66
+ 1. اسامی اشخاص → person-01, person-02, ...
67
+ 2. نام شرکت‌ها/سازمان‌ها → company-01, company-02, ...
68
+ 3. مقادیر پولی → amount-01, amount-02, ...
69
+ 4. درصدها → percent-01, percent-02, ...
70
+ 5. فقط این توکن‌ها استفاده کنید
71
+ 6. شماره‌های نسخه را درست حفظ کنید
72
+ 7. اگر موجودیت تکرار شود از شماره قدیمی استفاده کنید
73
+
74
+ متن:
75
+ {text}
76
+
77
+ خروجی: فقط متن ناشناس شده"""
78
+
79
+ response = requests.post(
80
+ "https://api.cerebras.ai/v1/chat/completions",
81
+ headers={
82
+ "Authorization": f"Bearer {self.cerebras_key}",
83
+ "Content-Type": "application/json"
84
+ },
85
+ json={
86
+ "model": "llama-3.3-70b",
87
+ "messages": [{"role": "user", "content": prompt}],
88
+ "max_tokens": 4096,
89
+ "temperature": 0.1
90
+ },
91
+ timeout=60
92
+ )
93
+
94
+ if response.status_code == 200:
95
+ anonymized_text = response.json()['choices'][0]['message']['content'].strip()
96
+ logger.info("✅ Cerebras: موفق")
97
+
98
+ # استخراج mapping از متن
99
+ self._extract_mapping_from_text(text, anonymized_text)
100
+ return anonymized_text, self.mapping_table
101
+ else:
102
+ logger.error(f"❌ Cerebras Error")
103
+ return self.anonymize_with_regex(text)
104
+
105
+ except Exception as e:
106
+ logger.error(f"❌ Cerebras Exception: {e}")
107
+ return self.anonymize_with_regex(text)
108
+
109
+ def anonymize_combined(self, text: str) -> Tuple[str, Dict]:
110
+ """روش ترکیبی: Regex + Cerebras"""
111
+ logger.info("⚙️ روش ترکیبی...")
112
+
113
+ # Step 1: Regex برای شناسایی سریع
114
+ anonymized_regex, _ = self.anonymize_with_regex(text)
115
+
116
+ # Step 2: اگر Cerebras موجود باشد، بهبود دهد
117
+ if self.cerebras_key:
118
+ try:
119
+ prompt = f"""متن اصلی:
120
+ {text}
121
+
122
+ متن ناشناس شده توسط Regex:
123
+ {anonymized_regex}
124
+
125
+ قوانین:
126
+ 1. تمام موجودیت‌های حساس را شناسایی کنید
127
+ 2. اگر Regex چیزی را از دست داده است، اضافه کنید
128
+ 3. شماره‌گذاری را حفظ کنید (person-01 همان person-01 بماند)
129
+ 4. تنها متن ناشناس شده را برگردانید"""
130
+
131
+ response = requests.post(
132
+ "https://api.cerebras.ai/v1/chat/completions",
133
+ headers={
134
+ "Authorization": f"Bearer {self.cerebras_key}",
135
+ "Content-Type": "application/json"
136
+ },
137
+ json={
138
+ "model": "llama-3.3-70b",
139
+ "messages": [{"role": "user", "content": prompt}],
140
+ "max_tokens": 4096,
141
+ "temperature": 0.1
142
+ },
143
+ timeout=60
144
+ )
145
+
146
+ if response.status_code == 200:
147
+ enhanced = response.json()['choices'][0]['message']['content'].strip()
148
+ self._extract_mapping_from_text(text, enhanced)
149
+ logger.info("✅ ترکیبی: بهبود شده")
150
+ return enhanced, self.mapping_table
151
+ except:
152
+ pass
153
+
154
+ logger.info("✅ ترکیبی: Regex نتیجه")
155
+ return anonymized_regex, self.mapping_table
156
+
157
+ def _extract_mapping_from_text(self, original: str, anonymized: str):
158
+ """استخراج mapping از متن‌های اصلی و ناشناس شده"""
159
+ # الگوهای موجودیت
160
+ patterns = {
161
+ 'person': r'\b[ء-ي]+\s+[ء-ي]+(?:\s+[ء-ي]+)*\b',
162
+ 'company': r'(?:شرکت|بانک|سازمان|گروه|هلدینگ)\s+[ء-ي]+(?:\s+[ء-ي]+)*',
163
+ 'amount': r'\d+\s*(?:میلیارد|میلیون|هزار|تومان|ریال|دلار|تن)',
164
+ 'percent': r'\d+\s*(?:درصد|%|درصدی)',
165
+ }
166
+
167
+ for entity_type, pattern in patterns.items():
168
+ for match in re.finditer(pattern, original):
169
+ text_match = match.group()
170
+ tokens = re.findall(f'{entity_type}-\\d+', anonymized)
171
+ if tokens:
172
+ for token in tokens:
173
+ if token not in self.mapping_table:
174
+ self.mapping_table[token] = text_match
175
+ self.reverse_mapping[text_match] = token
176
+
177
+ def analyze_with_gpt(self, anonymized_text: str) -> str:
178
+ """تحلیل متن ناشناس‌شده با ChatGPT"""
179
+ logger.info("🤖 ChatGPT تحلیل...")
180
+
181
+ if not self.gpt_key:
182
+ logger.warning("⚠️ GPT API Key نیست")
183
+ return "❌ API Key موجود نیست"
184
+
185
+ try:
186
+ prompt = f"""متن مالی ناشناس‌شده زیر را تحلیل و خلاصه کنید:
187
+
188
+ متن:
189
+ {anonymized_text}
190
+
191
+ لطفاً:
192
+ 1. خلاصه‌ای مختصر و معنادار بنویسید
193
+ 2. نکات اصلی را مشخص کنید
194
+ 3. تمام توکن‌های ناشناس (person-01, company-01, amount-01, percent-01) را حفظ کنید
195
+ 4. تنها اطلاعات موجود در متن را بیان کنید"""
196
+
197
+ response = requests.post(
198
+ "https://api.openai.com/v1/chat/completions",
199
+ headers={
200
+ "Authorization": f"Bearer {self.gpt_key}",
201
+ "Content-Type": "application/json"
202
+ },
203
+ json={
204
+ "model": "gpt-4o-mini",
205
+ "messages": [
206
+ {
207
+ "role": "system",
208
+ "content": "شما دستیار تحلیل متون مالی فارسی هستید. توکن‌های ناشناس را حفظ کنید."
209
+ },
210
+ {"role": "user", "content": prompt}
211
+ ],
212
+ "max_tokens": 1500,
213
+ "temperature": 0.7
214
+ },
215
+ timeout=60
216
+ )
217
+
218
+ if response.status_code == 200:
219
+ gpt_response = response.json()['choices'][0]['message']['content']
220
+ logger.info("✅ ChatGPT: تحلیل کامل")
221
+ return gpt_response
222
+ else:
223
+ logger.error(f"❌ GPT Error: {response.status_code}")
224
+ return "❌ خطای ChatGPT"
225
+
226
+ except Exception as e:
227
+ logger.error(f"❌ GPT Exception: {e}")
228
+ return f"❌ خطا: {str(e)}"
229
+
230
+ def restore_text(self, anonymized_text: str) -> str:
231
+ """بازگردانی متن اصلی"""
232
+ logger.info("🔄 بازگردانی...")
233
+
234
+ restored = anonymized_text
235
+ for placeholder, original in sorted(self.mapping_table.items()):
236
+ restored = restored.replace(placeholder, original)
237
+
238
+ logger.info("✅ بازگردانی کامل")
239
+ return restored
240
+
241
+ def get_mapping_table_md(self) -> str:
242
+ """تبدیل جدول نگاشت به Markdown"""
243
+ if not self.mapping_table:
244
+ return "### 📋 جدول نگاشت\n\nهیچ موجودیتی شناسایی نشد"
245
+
246
+ table = "### 📋 جدول نگاشت\n\n"
247
+ table += "| شناسه | متن اصلی |\n"
248
+ table += "|-------|----------|\n"
249
+
250
+ for token, original in sorted(self.mapping_table.items()):
251
+ table += f"| **{token}** | {original} |\n"
252
+
253
+ return table
254
+
255
+ # متغیر سراسری
256
+ anonymizer = None
257
+
258
+ def process(input_text: str, method: str = "combined"):
259
+ """پردازش متن - 4 مرحله"""
260
+ global anonymizer
261
+
262
+ if not input_text.strip():
263
+ return "", "", "", ""
264
+
265
+ cerebras_key = os.getenv("CEREBRAS_API_KEY")
266
+ gpt_key = os.getenv("OPENAI_API_KEY")
267
+
268
+ if not anonymizer:
269
+ anonymizer = AnonymizerAdvanced(cerebras_key, gpt_key)
270
+ else:
271
+ anonymizer.mapping_table = {}
272
+ anonymizer.reverse_mapping = {}
273
+
274
+ try:
275
+ logger.info("=" * 70)
276
+ logger.info(f"🚀 شروع پردازش - روش: {method}")
277
+ logger.info("=" * 70)
278
+
279
+ # ============================================
280
+ # مرحله 1: ناشناس‌سازی
281
+ # ============================================
282
+ logger.info("📝 مرحله 1: ناشناس‌سازی...")
283
+
284
+ if method == "regex":
285
+ anonymized_text, _ = anonymizer.anonymize_with_regex(input_text)
286
+ elif method == "cerebras":
287
+ anonymized_text, _ = anonymizer.anonymize_with_cerebras(input_text)
288
+ else: # combined
289
+ anonymized_text, _ = anonymizer.anonymize_combined(input_text)
290
+
291
+ logger.info(f"✅ ناشناس‌سازی: {len(anonymized_text)} کاراکتر")
292
+
293
+ # ============================================
294
+ # مرحله 2: تحلیل ChatGPT
295
+ # ============================================
296
+ logger.info("🤖 مرحله 2: تحلیل ChatGPT...")
297
+ gpt_response = anonymizer.analyze_with_gpt(anonymized_text)
298
+ logger.info(f"✅ تحلیل: {len(gpt_response)} کاراکتر")
299
+
300
+ # ============================================
301
+ # مرحله 3: بازگردانی
302
+ # ============================================
303
+ logger.info("🔄 مرحله 3: بازگردانی...")
304
+ restored_text = anonymizer.restore_text(anonymized_text)
305
+ logger.info("✅ بازگردانی کامل")
306
+
307
+ # ============================================
308
+ # مرحله 4: جدول نگاشت
309
+ # ============================================
310
+ logger.info("📋 مرحله 4: جدول نگاشت...")
311
+ mapping_str = anonymizer.get_mapping_table_md()
312
+ logger.info(f"✅ {len(anonymizer.mapping_table)} موجودیت")
313
+
314
+ logger.info("=" * 70)
315
+ logger.info("✅ تمام مراحل کامل!")
316
+ logger.info("=" * 70)
317
+
318
+ return anonymized_text, gpt_response, restored_text, mapping_str
319
+
320
+ except Exception as e:
321
+ logger.error(f"❌ خطا: {str(e)}", exc_info=True)
322
+ return "", f"❌ خطا: {str(e)}", "", ""
323
+
324
+ def clear_all():
325
+ """پاک کردن همه"""
326
+ return "", "", "", ""
327
+
328
+ # Gradio Interface
329
+ css_rtl = """
330
+ .input-box { direction: rtl; text-align: right; }
331
+ .textbox textarea { direction: rtl; text-align: right; font-family: 'Tahoma', serif; }
332
+ """
333
+
334
+ with gr.Blocks(title="سیستم ناشناس‌سازی متون", theme=gr.themes.Soft(), css=css_rtl) as app:
335
+
336
+ gr.Markdown("# 🔐 سیستم ناشناس‌سازی متون مالی فارسی", elem_classes="input-box")
337
+ gr.Markdown("#### استخراج و ناشناس‌سازی موجودیت‌های حساس", elem_classes="input-box")
338
+
339
+ # ============================================
340
+ # صفحه اول: ورودی (راست) + دکمه‌ها (چپ)
341
+ # ============================================
342
+ with gr.Row():
343
+ # سمت راست: متن ورودی (بزرگ‌تر)
344
+ with gr.Column(scale=3):
345
+ input_text = gr.Textbox(
346
+ lines=14,
347
+ placeholder="متن مالی/خبری را وارد کنید...",
348
+ label="📝 متن ورودی",
349
+ elem_classes="textbox"
350
+ )
351
+
352
+ # سمت چپ: دکمه‌ها و تنظیمات
353
+ with gr.Column(scale=1):
354
+ gr.Markdown("### ⚙️ تنظیمات", elem_classes="input-box")
355
+
356
+ method = gr.Radio(
357
+ ["regex", "cerebras", "combined"],
358
+ value="combined",
359
+ label="🔧 روش پردازش",
360
+ elem_classes="input-box"
361
+ )
362
+
363
+ gr.Markdown("---")
364
+
365
+ with gr.Column():
366
+ process_btn = gr.Button(
367
+ "▶️ پردازش",
368
+ variant="primary",
369
+ size="lg"
370
+ )
371
+
372
+ clear_btn = gr.Button(
373
+ "🗑️ پاک کردن",
374
+ variant="stop",
375
+ size="lg"
376
+ )
377
+
378
+ # ============================================
379
+ # صفحه دوم: 3 باکس نتایج (وسط)
380
+ # ============================================
381
+ gr.Markdown("---")
382
+ gr.Markdown("## 📊 نتایج پردازش", elem_classes="input-box")
383
+
384
+ with gr.Row():
385
+ # باکس 1: متن ناشناس‌شده (راست)
386
+ with gr.Column(scale=1):
387
+ anonymized_text = gr.Textbox(
388
+ lines=12,
389
+ label="🔒 متن ناشناس‌شده",
390
+ interactive=False,
391
+ elem_classes="textbox"
392
+ )
393
+
394
+ # باکس 2: تحلیل ChatGPT (وسط)
395
+ with gr.Column(scale=1):
396
+ gpt_analysis = gr.Textbox(
397
+ lines=12,
398
+ label="🤖 تحلیل ChatGPT",
399
+ interactive=False,
400
+ elem_classes="textbox"
401
+ )
402
+
403
+ # باکس 3: متن بازگردانی شده (چپ)
404
+ with gr.Column(scale=1):
405
+ restored_text = gr.Textbox(
406
+ lines=12,
407
+ label="✅ متن بازگردانی شده",
408
+ interactive=False,
409
+ elem_classes="textbox"
410
+ )
411
+
412
+ # ============================================
413
+ # پایین: جدول نگاشت
414
+ # ============================================
415
+ gr.Markdown("---")
416
+
417
+ mapping_table = gr.Textbox(
418
+ lines=10,
419
+ label="📋 جدول نگاشت",
420
+ interactive=False,
421
+ elem_classes="textbox"
422
+ )
423
+
424
+ # ============================================
425
+ # Event Handlers
426
+ # ============================================
427
+ process_btn.click(
428
+ fn=process,
429
+ inputs=[input_text, method],
430
+ outputs=[anonymized_text, gpt_analysis, restored_text, mapping_table]
431
+ )
432
+
433
+ clear_btn.click(
434
+ fn=clear_all,
435
+ outputs=[input_text, anonymized_text, gpt_analysis, restored_text, mapping_table]
436
+ )
437
+
438
+ if __name__ == "__main__":
439
+ print("\n" + "=" * 70)
440
+ print("🚀 سیستم ناشناس‌سازی متون در حال راه‌اندازی...")
441
+ print("=" * 70)
442
+ print("\n📋 نحوه استفاده:\n")
443
+ print("1. CEREBRAS_API_KEY و OPENAI_API_KEY را تنظیم کنید (اختیاری)")
444
+ print("2. http://localhost:7860 را باز کنید")
445
+ print("3. متن را وارد کنید")
446
+ print("4. روش را انتخاب کنید (توصیه: ترکیبی)")
447
+ print("5. 'پردازش' را کلیک کنید\n")
448
+ print("روش‌های دستیس:")
449
+ print(" ✅ regex: سریع، بدون API")
450
+ print(" ✅ cerebras: دقیق، با API Cerebras")
451
+ print(" ✅ combined: بهترین (ترکیبی)\n")
452
+ print("=" * 70 + "\n")
453
+
454
+ app.launch(
455
+ server_name="0.0.0.0",
456
+ server_port=7860,
457
+ share=False,
458
+ show_error=True
459
+ )