leilaghomashchi commited on
Commit
13c147d
·
verified ·
1 Parent(s): 7a4ca03

Upload app_rtl_fa_final_UPDATED.py

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