leilaghomashchi commited on
Commit
5c093de
·
verified ·
1 Parent(s): fa2743b

Delete app.py

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