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

Delete app2.py

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