leilaghomashchi commited on
Commit
7a4ca03
·
verified ·
1 Parent(s): 976de75

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -534
app.py DELETED
@@ -1,534 +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
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
-
23
- def anonymize_with_cerebras(self, text: str) -> Tuple[str, Dict]:
24
- """ناشناس‌سازی با Cerebras"""
25
- logger.info("🧠 روش Cerebras...")
26
-
27
- if not self.cerebras_key:
28
- logger.error("❌ Cerebras API Key موجود نیست")
29
- raise ValueError("Cerebras API Key مورد نیاز است")
30
-
31
- try:
32
- prompt = f"""متن زیر را ناشناس کنید. قوانین:
33
- 1. اسامی اشخاص → person-01, person-02, ...
34
- 2. نام شرکت‌ها/سازمان‌ها → company-01, company-02, ...
35
- 3. مقادیر پولی → amount-01, amount-02, ...
36
- 4. درصدها → percent-01, percent-02, ...
37
- 5. فقط این توکن‌ها استفاده کنید
38
- 6. شماره‌های نسخه را درست حفظ کنید
39
- 7. اگر موجودیت تکرار شود از شماره قدیمی استفاده کنید
40
-
41
- متن:
42
- {text}
43
-
44
- خروجی: فقط متن ناشناس شده"""
45
-
46
- response = requests.post(
47
- "https://api.cerebras.ai/v1/chat/completions",
48
- headers={
49
- "Authorization": f"Bearer {self.cerebras_key}",
50
- "Content-Type": "application/json"
51
- },
52
- json={
53
- "model": "llama-3.3-70b",
54
- "messages": [{"role": "user", "content": prompt}],
55
- "max_tokens": 4096,
56
- "temperature": 0.1
57
- },
58
- timeout=60
59
- )
60
-
61
- if response.status_code == 200:
62
- anonymized_text = response.json()['choices'][0]['message']['content'].strip()
63
- logger.info("✅ Cerebras: موفق")
64
-
65
- # استخراج mapping از متن
66
- self._extract_mapping_from_text(text, anonymized_text)
67
- return anonymized_text, self.mapping_table
68
- else:
69
- logger.error(f"❌ Cerebras Error: {response.status_code}")
70
- raise Exception(f"Cerebras API Error: {response.status_code}")
71
-
72
- except Exception as e:
73
- logger.error(f"❌ Cerebras Exception: {e}")
74
- raise
75
-
76
-
77
- def _extract_mapping_from_text(self, original: str, anonymized: str):
78
- """استخراج mapping از متن‌های اصلی و ناشناس شده"""
79
- # الگوهای موجودیت
80
- patterns = {
81
- 'person': r'\b[ء-ي]+\s+[ء-ي]+(?:\s+[ء-ي]+)*\b',
82
- 'company': r'(?:شرکت|بانک|سازمان|گروه|هلدینگ)\s+[ء-ي]+(?:\s+[ء-ي]+)*',
83
- 'amount': r'\d+\s*(?:میلیارد|میلیون|هزار|تومان|ریال|دلار|تن)',
84
- 'percent': r'\d+\s*(?:درصد|%|درصدی)',
85
- }
86
-
87
- for entity_type, pattern in patterns.items():
88
- for match in re.finditer(pattern, original):
89
- text_match = match.group()
90
- tokens = re.findall(f'{entity_type}-\\d+', anonymized)
91
- if tokens:
92
- for token in tokens:
93
- if token not in self.mapping_table:
94
- self.mapping_table[token] = text_match
95
- self.reverse_mapping[text_match] = token
96
-
97
- def _extract_tokens_from_text(self, text: str) -> List[str]:
98
- """استخراج تمام توکن‌های ناشناس از متن"""
99
- pattern = r'(person-\d+|company-\d+|amount-\d+|percent-\d+)'
100
- return sorted(set(re.findall(pattern, text)))
101
-
102
- def _validate_tokens_in_response(self, original_tokens: List[str], response_text: str) -> Tuple[bool, List[str], List[str]]:
103
- """بررسی اینکه تمام توکن‌های ناشناس در پاسخ محفوظ ماندند"""
104
- response_tokens = self._extract_tokens_from_text(response_text)
105
-
106
- missing_tokens = [t for t in original_tokens if t not in response_tokens]
107
- extra_tokens = [t for t in response_tokens if t not in original_tokens]
108
-
109
- is_valid = len(missing_tokens) == 0 and len(extra_tokens) == 0
110
-
111
- return is_valid, missing_tokens, extra_tokens
112
-
113
- def analyze_with_gpt(self, anonymized_text: str, analysis_prompt: str = None, max_retries: int = 2) -> str:
114
- """
115
- اجرای پرامپت‌های درون متن ناشناس‌سازی شده با ChatGPT
116
-
117
- 🎯 مراحل الگوریتم:
118
- 1. اعتبارسنجی ورودی‌های اولیه
119
- 2. استخراج و ثبت توکن‌های ناشناس
120
- 3. ایجاد پرامپت‌های بهینه‌شده (system + user)
121
- 4. ارسال درخواست به ChatGPT API
122
- 5. اعتبارسنجی توکن‌ها در پاسخ
123
- 6. مدیریت خطاها و retry خودکار
124
- """
125
- logger.info("🤖 ChatGPT: شروع پردازش...")
126
-
127
- # ============ مرحله 1: اعتبارسنجی ورودی ============
128
- if not self.gpt_key:
129
- error_msg = "❌ GPT API Key موجود نیست. متغیر OPENAI_API_KEY را تنظیم کنید."
130
- logger.error(error_msg)
131
- return error_msg
132
-
133
- if not anonymized_text or not anonymized_text.strip():
134
- error_msg = "❌ متن ناشناس‌سازی شده خالی است"
135
- logger.error(error_msg)
136
- return error_msg
137
-
138
- # ============ مرحله 2: استخراج و ثبت توکن‌های اصلی ============
139
- original_tokens = self._extract_tokens_from_text(anonymized_text)
140
- logger.info(f"🔍 توکن‌های شناسایی شده ({len(original_tokens)}): {original_tokens}")
141
-
142
- # ============ مرحله 3: ایجاد پرامپت‌های بهینه‌شده ============
143
- if not analysis_prompt:
144
- analysis_prompt = """لطفاً این متن را تحلیل کنید:
145
- 1. خلاصه اطلاعات مالی
146
- 2. کیان‌های اصلی (با توکن‌های ناشناس شده)
147
- 3. رابطه‌های مهم
148
- 4. داده‌های عددی و درصدها"""
149
-
150
- # 📋 پرامپت سیستمی بهتر شده - دستورات دقیق
151
- system_prompt = """شما دستیار تحلیل متون مالی‌اند که:
152
-
153
- 📋 قوانین اجباری:
154
- 1. تمام توکن‌های ناشناس (person-01, company-01, amount-01, percent-01) را **دقیقاً** همان‌طور حفظ کنید
155
- 2. توکن‌ها را هرگز تغییر ندهید، تغییر نام ندهید یا حذف نکنید
156
- 3. توکن‌ها را در متن خروجی دقیقاً مثل ورودی استفاده کنید
157
-
158
- 🎯 دستورات:
159
- 1. متن‌های ناشناس‌سازی شده را با دقت تحلیل کنید
160
- 2. دستورات کاربر را اجرا کنید
161
- 3. فقط نتیجه تحلیل را برگردانید بدون توضیح اضافی
162
-
163
- ⚠️ مهم: اگر توکنی را حذف یا تغییر دادید، پاسخ نامعتبر است"""
164
-
165
- # 👤 پیام کاربری بهتر شده
166
- user_message = f"""📝 متن ناشناس‌سازی شده:
167
- ---
168
- {anonymized_text}
169
- ---
170
-
171
- 🎯 دستورات تحلیلی:
172
- {analysis_prompt}
173
-
174
- ⚠️ هشدار: تمام توکن‌های ناشناس را درست‌نویسی شده حفظ کنید!
175
- 📊 توکن‌های مورد انتظار: {', '.join(original_tokens) if original_tokens else 'بدون توکن'}
176
-
177
- 📤 خروجی: فقط نتیجه تحلیل (بدون توضیح اضافی)"""
178
-
179
- logger.info(f"📨 آماده‌سازی درخواست...")
180
- logger.debug(f"🎯 System Prompt:\n{system_prompt}\n")
181
- logger.debug(f"👤 User Message:\n{user_message}\n")
182
-
183
- # ============ مرحله 4: ارسال درخواست با Retry ============
184
- for attempt in range(max_retries + 1):
185
- try:
186
- logger.info(f"🔄 تلاش {attempt + 1}/{max_retries + 1}...")
187
-
188
- response = requests.post(
189
- "https://api.openai.com/v1/chat/completions",
190
- headers={
191
- "Authorization": f"Bearer {self.gpt_key}",
192
- "Content-Type": "application/json",
193
- "User-Agent": "AnonymizerAdvanced/1.0"
194
- },
195
- json={
196
- "model": "gpt-4o-mini",
197
- "messages": [
198
- {
199
- "role": "system",
200
- "content": system_prompt
201
- },
202
- {
203
- "role": "user",
204
- "content": user_message
205
- }
206
- ],
207
- "max_tokens": 4096,
208
- "temperature": 0.3,
209
- "top_p": 0.9,
210
- "frequency_penalty": 0.0,
211
- "presence_penalty": 0.0
212
- },
213
- timeout=90
214
- )
215
-
216
- # ============ مرحله 5: پردازش پاسخ ============
217
- if response.status_code == 200:
218
- response_data = response.json()
219
-
220
- if 'choices' not in response_data or not response_data['choices']:
221
- logger.error("❌ پاسخ ChatGPT خالی است")
222
- if attempt < max_retries:
223
- logger.info("🔄 دوباره تلاش می‌کنیم...")
224
- continue
225
- return "❌ پاسخ ChatGPT خالی است"
226
-
227
- gpt_response = response_data['choices'][0]['message']['content'].strip()
228
-
229
- logger.info(f"✅ پاسخ دریافت شد ({len(gpt_response)} کاراکتر)")
230
- logger.debug(f"📤 پاسخ (اولین 500 کاراکتر):\n{gpt_response[:500]}...\n")
231
-
232
- # ============ مرحله 6: اعتبارسنجی توکن‌ها ============
233
- is_valid, missing, extra = self._validate_tokens_in_response(original_tokens, gpt_response)
234
-
235
- if is_valid:
236
- logger.info("✅ تمام توکن‌ها صحیح محفوظ شدند")
237
- return gpt_response
238
- else:
239
- logger.warning(f"⚠️ مشکل در توکن‌ها:")
240
- if missing:
241
- logger.warning(f" - توکن‌های حذف شده: {missing}")
242
- if extra:
243
- logger.warning(f" - توکن‌های اضافی: {extra}")
244
-
245
- if attempt < max_retries:
246
- logger.info(f"🔄 دوباره تلاش می‌کنیم...")
247
- continue
248
- else:
249
- logger.error("❌ توکن‌ها در پاسخ مطابقت ندارد")
250
- warning_msg = f"⚠️ هشدار: برخی توکن‌ها درست نشدند\n\n{gpt_response}"
251
- return warning_msg
252
-
253
- else:
254
- # ============ مدیریت خطاهای HTTP ============
255
- error_data = response.json() if response.content else {}
256
- error_message = error_data.get('error', {}).get('message', response.text)
257
-
258
- logger.error(f"❌ GPT API Error ({response.status_code}): {error_message}")
259
-
260
- # تفاوت‌های خاص برای هر نوع خطا
261
- if response.status_code == 401:
262
- return "❌ خطای احراز هویت: کلید API نامعتبر است"
263
- elif response.status_code == 429:
264
- logger.info("⏳ سهمیه تمام شده، 5 ثانیه صبر...")
265
- import time
266
- if attempt < max_retries:
267
- time.sleep(5)
268
- continue
269
- return "❌ سهمیه API تمام شده است"
270
- elif response.status_code == 500:
271
- logger.info("🔄 خطای سرور OpenAI، دوباره تلاش...")
272
- if attempt < max_retries:
273
- continue
274
- return "❌ خطای سرور OpenAI"
275
- else:
276
- if attempt < max_retries:
277
- logger.info("🔄 دوباره تلاش می‌کنیم...")
278
- continue
279
- return f"❌ خطای ChatGPT ({response.status_code}): {error_message}"
280
-
281
- except requests.Timeout:
282
- logger.error("❌ Timeout: درخواست منقضی شد")
283
- if attempt < max_retries:
284
- logger.info("🔄 دوباره تلاش می‌کنیم...")
285
- continue
286
- return "❌ Timeout: پاسخ ChatGPT تاخیر بیش‌ازحد دارد"
287
-
288
- except requests.RequestException as e:
289
- logger.error(f"❌ Request Error: {str(e)}")
290
- if attempt < max_retries:
291
- logger.info("🔄 دوباره تلاش می‌کنیم...")
292
- continue
293
- return f"❌ خطای ارتباط: {str(e)}"
294
-
295
- except Exception as e:
296
- logger.error(f"❌ Exception: {str(e)}", exc_info=True)
297
- if attempt < max_retries:
298
- logger.info("🔄 دوباره تلاش می‌کنیم...")
299
- continue
300
- return f"❌ خطای غیرمنتظره: {str(e)}"
301
-
302
- return "❌ ناموفق بعد از تمام تلاش‌ها"
303
-
304
- def restore_text(self, anonymized_text: str) -> str:
305
- """بازگردانی متن اصلی"""
306
- logger.info("🔄 بازگردانی...")
307
-
308
- restored = anonymized_text
309
- for placeholder, original in sorted(self.mapping_table.items()):
310
- restored = restored.replace(placeholder, original)
311
-
312
- logger.info("✅ بازگردانی کامل")
313
- return restored
314
-
315
- def get_mapping_table_md(self) -> str:
316
- """تبدیل جدول نگاشت به Markdown"""
317
- if not self.mapping_table:
318
- return "### 📋 جدول نگاشت\n\nهیچ موجودیتی شناسایی نشد"
319
-
320
- table = "### 📋 جدول نگاشت\n\n"
321
- table += "| شناسه | متن اصلی |\n"
322
- table += "|-------|----------|\n"
323
-
324
- for token, original in sorted(self.mapping_table.items()):
325
- table += f"| **{token}** | {original} |\n"
326
-
327
- return table
328
-
329
- # متغیر سراسری
330
- anonymizer = None
331
-
332
- def process(input_text: str, analysis_prompt: str = None, method: str = "cerebras"):
333
- """پردازش متن - 4 مرحله"""
334
- global anonymizer
335
-
336
- if not input_text.strip():
337
- return "", "", "", ""
338
-
339
- cerebras_key = os.getenv("CEREBRAS_API_KEY")
340
- gpt_key = os.getenv("OPENAI_API_KEY")
341
-
342
- if not anonymizer:
343
- anonymizer = AnonymizerAdvanced(cerebras_key, gpt_key)
344
- else:
345
- anonymizer.mapping_table = {}
346
- anonymizer.reverse_mapping = {}
347
-
348
- try:
349
- logger.info("=" * 70)
350
- logger.info(f"🚀 شروع پردازش - روش: Cerebras")
351
- logger.info("=" * 70)
352
-
353
- # ============================================
354
- # مرحله 1: ناشناس‌سازی
355
- # ============================================
356
- logger.info("📝 مرحله 1: ناشناس‌سازی...")
357
-
358
- anonymized_text, _ = anonymizer.anonymize_with_cerebras(input_text)
359
-
360
- logger.info(f"✅ ناشناس‌سازی: {len(anonymized_text)} کاراکتر")
361
-
362
- # ============================================
363
- # مرحله 2: ChatGPT با متن ناشناس‌سازی شده + دستورات
364
- # ============================================
365
- logger.info("🤖 مرحله 2: ChatGPT...")
366
- gpt_response = anonymizer.analyze_with_gpt(anonymized_text, analysis_prompt)
367
- logger.info(f"✅ ChatGPT: {len(gpt_response)} کاراکتر")
368
-
369
- # ============================================
370
- # مرحله 3: بازگردانی پاسخ ChatGPT
371
- # ============================================
372
- logger.info("🔄 مرحله 3: بازگردانی...")
373
- restored_text = anonymizer.restore_text(gpt_response)
374
- logger.info("✅ بازگردانی کامل")
375
-
376
- # ============================================
377
- # مرحله 4: جدول نگاشت
378
- # ============================================
379
- logger.info("📋 مرحله 4: جدول نگاشت...")
380
- mapping_str = anonymizer.get_mapping_table_md()
381
- logger.info(f"✅ {len(anonymizer.mapping_table)} موجودیت")
382
-
383
- logger.info("=" * 70)
384
- logger.info("✅ تمام مراحل کامل!")
385
- logger.info("=" * 70)
386
-
387
- return restored_text, gpt_response, anonymized_text, mapping_str
388
-
389
- except Exception as e:
390
- logger.error(f"❌ خطا: {str(e)}", exc_info=True)
391
- return "", f"❌ خطا: {str(e)}", "", ""
392
-
393
- def clear_all():
394
- """پاک کردن همه"""
395
- return "", "", "", "", "", ""
396
-
397
- # Gradio Interface
398
- css_rtl = """
399
- .input-box { direction: rtl; text-align: right; }
400
- .textbox textarea { direction: rtl; text-align: right; font-family: 'Tahoma', serif; }
401
- """
402
-
403
- with gr.Blocks(title="سیستم ناشناس‌سازی متون", theme=gr.themes.Soft(), css=css_rtl) as app:
404
-
405
- gr.Markdown("# 🔐 سیستم ناشناس‌سازی متون مالی فارسی", elem_classes="input-box")
406
- gr.Markdown("#### استخراج و ناشناس‌سازی موجودیت‌های حساس", elem_classes="input-box")
407
-
408
- # ============================================
409
- # صفحه اول: دکمه‌ها (راست) + ورودی (چپ)
410
- # ============================================
411
- with gr.Row():
412
- # سمت راست: دکمه‌ها و تنظیمات
413
- with gr.Column(scale=1):
414
- gr.Markdown("### ⚙️ تنظیمات", elem_classes="input-box")
415
-
416
- method = gr.Radio(
417
- ["cerebras"],
418
- value="cerebras",
419
- label="🔧 روش پردازش",
420
- elem_classes="input-box"
421
- )
422
-
423
- gr.Markdown("---")
424
-
425
- analysis_prompt = gr.Textbox(
426
- lines=8,
427
- placeholder="دستورات تحلیلی برای ChatGPT...",
428
- label="📋 دست��رات تحلیلی",
429
- elem_classes="textbox",
430
- value="خلاصه سازی\nشناسایی موجودیت‌های ناشناس\nنکات کلیدی"
431
- )
432
-
433
- gr.Markdown("---")
434
-
435
- with gr.Column():
436
- process_btn = gr.Button(
437
- "▶️ پردازش",
438
- variant="primary",
439
- size="lg"
440
- )
441
-
442
- clear_btn = gr.Button(
443
- "🗑️ پاک کردن",
444
- variant="stop",
445
- size="lg"
446
- )
447
-
448
- # سمت چپ: متن ورودی (بزرگ‌تر)
449
- with gr.Column(scale=3):
450
- input_text = gr.Textbox(
451
- lines=14,
452
- placeholder="متن مالی/خبری را وارد کنید...",
453
- label="📝 متن ورودی",
454
- elem_classes="textbox"
455
- )
456
-
457
- # ============================================
458
- # صفحه دوم: 3 باکس نتایج (وسط)
459
- # ============================================
460
- gr.Markdown("---")
461
- gr.Markdown("## 📊 نتایج پردازش", elem_classes="input-box")
462
-
463
- with gr.Row():
464
- # باکس 1: متن بازگردانی شده (راست)
465
- with gr.Column(scale=1):
466
- restored_text = gr.Textbox(
467
- lines=12,
468
- label="✅ متن بازگردانی شده",
469
- interactive=False,
470
- elem_classes="textbox"
471
- )
472
-
473
- # باکس 2: تحلیل ChatGPT (وسط)
474
- with gr.Column(scale=1):
475
- gpt_analysis = gr.Textbox(
476
- lines=12,
477
- label="🤖 تحلیل ChatGPT",
478
- interactive=False,
479
- elem_classes="textbox"
480
- )
481
-
482
- # باکس 3: متن ناشناس‌شده (چپ)
483
- with gr.Column(scale=1):
484
- anonymized_text = gr.Textbox(
485
- lines=12,
486
- label="🔒 متن ناشناس‌شده",
487
- interactive=False,
488
- elem_classes="textbox"
489
- )
490
-
491
- # ============================================
492
- # پایین: جدول نگاشت
493
- # ============================================
494
- gr.Markdown("---")
495
-
496
- mapping_table = gr.Textbox(
497
- lines=10,
498
- label="📋 جدول نگاشت",
499
- interactive=False,
500
- elem_classes="textbox"
501
- )
502
-
503
- # ============================================
504
- # Event Handlers
505
- # ============================================
506
- process_btn.click(
507
- fn=process,
508
- inputs=[input_text, analysis_prompt, method],
509
- outputs=[restored_text, gpt_analysis, anonymized_text, mapping_table]
510
- )
511
-
512
- clear_btn.click(
513
- fn=clear_all,
514
- outputs=[input_text, analysis_prompt, restored_text, gpt_analysis, anonymized_text, mapping_table]
515
- )
516
-
517
- if __name__ == "__main__":
518
- print("=" * 70)
519
- print("🚀 سیستم ناشناس‌سازی متون در حال راه‌اندازی...")
520
- print("=" * 70)
521
- print("\n📋 نحوه استفاده:\n")
522
- print("1. CEREBRAS_API_KEY و OPENAI_API_KEY را تنظیم کنید")
523
- print("2. http://localhost:7860 را باز کنید")
524
- print("3. متن را وارد کنید")
525
- print("4. 'پردازش' را کلیک کنید\n")
526
- print("روش استفاده شده: Cerebras (Llama 3.3-70B)")
527
- print("=" * 70 + "\n")
528
-
529
- app.launch(
530
- server_name="0.0.0.0",
531
- server_port=7860,
532
- share=False,
533
- show_error=True
534
- )