KashefTech commited on
Commit
d96d45c
·
verified ·
1 Parent(s): 50b0ee0

Delete app1.py

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