leilaghomashchi commited on
Commit
373cf25
·
verified ·
1 Parent(s): 864c0cd

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -728
app.py DELETED
@@ -1,728 +0,0 @@
1
- import gradio as gr
2
- import re
3
- import os
4
- import requests
5
- import json
6
- import logging
7
- import time
8
- from typing import Dict, List, Tuple, Optional
9
- from chatgpt_sender import ChatGPTSender
10
-
11
- logging.basicConfig(level=logging.INFO)
12
- logger = logging.getLogger(__name__)
13
-
14
- class MultiModelSender:
15
- """کلاس واحد برای ارسال به مدل‌های مختلف با retry mechanism"""
16
-
17
- def __init__(self):
18
- # دریافت API Keys از environment و trim کردن آنها
19
- self.openai_key = os.getenv("OPENAI_API_KEY", "").strip() if os.getenv("OPENAI_API_KEY") else None
20
- self.grok_key = os.getenv("XAI_API_KEY", "").strip() if os.getenv("XAI_API_KEY") else None
21
- self.gemini_key = os.getenv("GEMINI_API_KEY", "").strip() if os.getenv("GEMINI_API_KEY") else None
22
- self.deepseek_key = os.getenv("DEEPSEEK_API_KEY", "").strip() if os.getenv("DEEPSEEK_API_KEY") else None
23
-
24
- # ایجاد instance از ChatGPTSender
25
- self.chatgpt_sender = ChatGPTSender(api_key=self.openai_key, model="gpt-4o-mini")
26
-
27
- logger.info("✅ MultiModelSender مقداردهی شد")
28
-
29
- def send_to_chatgpt(self, text: str, system_msg: Optional[str] = None) -> str:
30
- """
31
- ارسال به ChatGPT با استفاده از ماژول پیشرفته
32
- """
33
- try:
34
- if system_msg:
35
- return self.chatgpt_sender.send(
36
- text=text,
37
- system_msg=system_msg,
38
- max_tokens=4096,
39
- temperature=0.1,
40
- lang='fa',
41
- retry_count=3
42
- )
43
- else:
44
- return self.chatgpt_sender.send_simple(text, lang='fa')
45
- except Exception as e:
46
- logger.error(f"❌ ChatGPT Error: {e}")
47
- return f"❌ خطا در ChatGPT: {str(e)}"
48
-
49
- def send_to_grok(self, text: str, system_msg: Optional[str] = None, retry_count: int = 3) -> str:
50
- """ارسال به Grok (xAI) با retry mechanism"""
51
- if not self.grok_key:
52
- return "❌ XAI_API_KEY (Grok) موجود نیست"
53
-
54
- messages = []
55
- if system_msg:
56
- messages.append({"role": "system", "content": system_msg})
57
- messages.append({"role": "user", "content": text})
58
-
59
- for attempt in range(retry_count):
60
- try:
61
- logger.info(f"📤 ارسال به Grok (تلاش {attempt + 1}/{retry_count})...")
62
-
63
- response = requests.post(
64
- "https://api.x.ai/v1/chat/completions",
65
- headers={
66
- "Authorization": f"Bearer {self.grok_key}",
67
- "Content-Type": "application/json"
68
- },
69
- json={
70
- "model": "grok-beta",
71
- "messages": messages,
72
- "temperature": 0.1,
73
- "max_tokens": 4096
74
- },
75
- timeout=60
76
- )
77
-
78
- if response.status_code == 200:
79
- logger.info("✅ Grok: پاسخ دریافت شد")
80
- return response.json()['choices'][0]['message']['content'].strip()
81
-
82
- elif response.status_code == 429:
83
- wait_time = 5 * (attempt + 1)
84
- logger.warning(f"⚠️ Grok Rate limit | صبر: {wait_time} ثانیه")
85
- if attempt < retry_count - 1:
86
- time.sleep(wait_time)
87
- continue
88
- else:
89
- return "❌ سهمیه Grok تمام شده است"
90
-
91
- elif response.status_code == 401:
92
- return "❌ کلید API Grok نامعتبر است!"
93
-
94
- elif response.status_code in [502, 503, 504]:
95
- wait_time = 2 * (attempt + 1)
96
- logger.warning(f"⚠️ Grok Server error | صبر: {wait_time} ثانیه")
97
- if attempt < retry_count - 1:
98
- time.sleep(wait_time)
99
- continue
100
- else:
101
- return f"❌ خطای سرور Grok: {response.status_code}"
102
-
103
- else:
104
- error_msg = response.text
105
- logger.error(f"❌ Grok API Error: {error_msg}")
106
- return f"❌ Grok API Error: {response.status_code}"
107
-
108
- except requests.exceptions.Timeout:
109
- logger.warning("⚠️ Grok Timeout | تلاش مجدد...")
110
- if attempt < retry_count - 1:
111
- time.sleep(3)
112
- continue
113
- else:
114
- return "❌ خطای اتصال به Grok: timeout"
115
-
116
- except Exception as e:
117
- logger.error(f"❌ Grok Exception: {e}")
118
- if attempt < retry_count - 1:
119
- time.sleep(2)
120
- continue
121
- else:
122
- return f"❌ خطا در Grok: {str(e)}"
123
-
124
- return "❌ خطای ناشناخته در Grok"
125
- def send_to_gemini(self, text: str, system_msg: Optional[str] = None, retry_count: int = 3) -> str:
126
- """ارسال به Google Gemini با retry mechanism"""
127
- if not self.gemini_key:
128
- return "❌ GEMINI_API_KEY موجود نیست"
129
-
130
- # 🔹 مدل پیشنهادی جدید (می‌توانی به gemini-2.5-pro هم تغییر بدهی)
131
- GEMINI_MODEL = "gemini-2.5-flash"
132
-
133
- # ترکیب system message و text
134
- full_prompt = f"{system_msg}\n\n{text}" if system_msg else text
135
-
136
- for attempt in range(retry_count):
137
- try:
138
- logger.info(f"📤 ارسال به Gemini (تلاش {attempt + 1}/{retry_count})...")
139
-
140
- # آدرس جدید + هدر استاندارد
141
- url = f"https://generativelanguage.googleapis.com/v1beta/models/{GEMINI_MODEL}:generateContent"
142
-
143
- response = requests.post(
144
- url,
145
- headers={
146
- "Content-Type": "application/json",
147
- "x-goog-api-key": self.gemini_key
148
- },
149
- json={
150
- "contents": [{
151
- "parts": [{"text": full_prompt}]
152
- }],
153
- "generationConfig": {
154
- "temperature": 0.1,
155
- "maxOutputTokens": 4096
156
- }
157
- },
158
- timeout=60
159
- )
160
-
161
- if response.status_code == 200:
162
- result = response.json()
163
- logger.info("✅ Gemini: پاسخ دریافت شد")
164
- return result['candidates'][0]['content']['parts'][0]['text'].strip()
165
-
166
- elif response.status_code == 429:
167
- wait_time = 5 * (attempt + 1)
168
- logger.warning(f"⚠️ Gemini Rate limit | صبر: {wait_time} ثانیه")
169
- if attempt < retry_count - 1:
170
- time.sleep(wait_time)
171
- continue
172
- else:
173
- return "❌ سهمیه Gemini تمام شده است"
174
-
175
- elif response.status_code == 400:
176
- error_data = response.json()
177
- error_msg = error_data.get('error', {}).get('message', 'خطای نامعتبر')
178
- return f"❌ خطای Gemini: {error_msg}"
179
-
180
- elif response.status_code in [502, 503, 504]:
181
- wait_time = 2 * (attempt + 1)
182
- logger.warning(f"⚠️ Gemini Server error | صبر: {wait_time} ثانیه")
183
- if attempt < retry_count - 1:
184
- time.sleep(wait_time)
185
- continue
186
- else:
187
- return f"❌ خطای سرور Gemini: {response.status_code}"
188
-
189
- else:
190
- # برای دیباگ بهتر، بدنه خطا را لاگ کن
191
- error_msg = response.text
192
- logger.error(f"❌ Gemini API Error: {error_msg}")
193
- return f"❌ Gemini API Error: {response.status_code}"
194
-
195
- except requests.exceptions.Timeout:
196
- logger.warning("⚠️ Gemini Timeout | تلاش مجدد...")
197
- if attempt < retry_count - 1:
198
- time.sleep(3)
199
- continue
200
- else:
201
- return "❌ خطای اتصال به Gemini: timeout"
202
-
203
- except Exception as e:
204
- logger.error(f"❌ Gemini Exception: {e}")
205
- if attempt < retry_count - 1:
206
- time.sleep(2)
207
- continue
208
- else:
209
- return f"❌ خطا در Gemini: {str(e)}"
210
-
211
- return "❌ خطای ناشناخته در Gemini"
212
-
213
-
214
- def send_to_deepseek(self, text: str, system_msg: Optional[str] = None, retry_count: int = 3) -> str:
215
- """ارسال به DeepSeek با retry mechanism"""
216
- if not self.deepseek_key:
217
- return "❌ DEEPSEEK_API_KEY موجود نیست"
218
-
219
- messages = []
220
- if system_msg:
221
- messages.append({"role": "system", "content": system_msg})
222
- messages.append({"role": "user", "content": text})
223
-
224
- for attempt in range(retry_count):
225
- try:
226
- logger.info(f"📤 ارسال به DeepSeek (تلاش {attempt + 1}/{retry_count})...")
227
-
228
- response = requests.post(
229
- "https://api.deepseek.com/v1/chat/completions",
230
- headers={
231
- "Authorization": f"Bearer {self.deepseek_key}",
232
- "Content-Type": "application/json"
233
- },
234
- json={
235
- "model": "deepseek-chat",
236
- "messages": messages,
237
- "temperature": 0.1,
238
- "max_tokens": 4096
239
- },
240
- timeout=60
241
- )
242
-
243
- if response.status_code == 200:
244
- logger.info("✅ DeepSeek: پاسخ دریافت شد")
245
- return response.json()['choices'][0]['message']['content'].strip()
246
-
247
- elif response.status_code == 429:
248
- wait_time = 5 * (attempt + 1)
249
- logger.warning(f"⚠️ DeepSeek Rate limit | صبر: {wait_time} ثانیه")
250
- if attempt < retry_count - 1:
251
- time.sleep(wait_time)
252
- continue
253
- else:
254
- return "❌ سهمیه DeepSeek تمام شده است"
255
-
256
- elif response.status_code == 401:
257
- return "❌ کلید API DeepSeek نامعتبر است!"
258
-
259
- elif response.status_code in [502, 503, 504]:
260
- wait_time = 2 * (attempt + 1)
261
- logger.warning(f"⚠️ DeepSeek Server error | صبر: {wait_time} ثانیه")
262
- if attempt < retry_count - 1:
263
- time.sleep(wait_time)
264
- continue
265
- else:
266
- return f"❌ خطای سرور DeepSeek: {response.status_code}"
267
-
268
- else:
269
- error_msg = response.text
270
- logger.error(f"❌ DeepSeek API Error: {error_msg}")
271
- return f"❌ DeepSeek API Error: {response.status_code}"
272
-
273
- except requests.exceptions.Timeout:
274
- logger.warning("⚠️ DeepSeek Timeout | تلاش مجدد...")
275
- if attempt < retry_count - 1:
276
- time.sleep(3)
277
- continue
278
- else:
279
- return "❌ خطای اتصال به DeepSeek: timeout"
280
-
281
- except Exception as e:
282
- logger.error(f"❌ DeepSeek Exception: {e}")
283
- if attempt < retry_count - 1:
284
- time.sleep(2)
285
- continue
286
- else:
287
- return f"❌ خطا در DeepSeek: {str(e)}"
288
-
289
- return "❌ خطای ناشناخته در DeepSeek"
290
-
291
- def send_message(self, text: str, model_name: str, system_msg: Optional[str] = None) -> str:
292
- """
293
- ارسال پیام به مدل انتخاب شده
294
-
295
- Args:
296
- text: متن ارسالی
297
- model_name: نام مدل
298
- system_msg: پیام سیستم (اختیاری)
299
-
300
- Returns:
301
- پاسخ مدل
302
- """
303
- logger.info(f"🤖 ارسال به {model_name}...")
304
-
305
- if model_name == "ChatGPT-4o-mini":
306
- return self.send_to_chatgpt(text, system_msg)
307
- elif model_name == "Grok":
308
- return self.send_to_grok(text, system_msg)
309
- elif model_name == "Gemini":
310
- return self.send_to_gemini(text, system_msg)
311
- elif model_name == "DeepSeek":
312
- return self.send_to_deepseek(text, system_msg)
313
- else:
314
- return f"❌ مدل نامعتبر: {model_name}"
315
-
316
-
317
- class AnonymizerAdvanced:
318
- """ناشناس‌ساز پیشرفته با Cerebras"""
319
-
320
- def __init__(self, cerebras_key: str = None):
321
- self.cerebras_key = cerebras_key or os.getenv("CEREBRAS_API_KEY")
322
- self.mapping_table = {}
323
- self.reverse_mapping = {}
324
-
325
- # ایجاد instance از MultiModelSender
326
- self.model_sender = MultiModelSender()
327
-
328
- logger.info("✅ Anonymizer Advanced مقداردهی شد")
329
-
330
- def anonymize_with_cerebras(self, text: str) -> Tuple[str, Dict]:
331
- """ناشناس‌سازی با Cerebras - دریافت mapping از مدل"""
332
- logger.info("🧠 روش Cerebras...")
333
-
334
- if not self.cerebras_key:
335
- logger.error("❌ Cerebras API Key موجود نیست")
336
- raise ValueError("Cerebras API Key مورد نیاز است")
337
-
338
- try:
339
- # مرحله 1: ناشناس‌سازی متن
340
- prompt1 = f"""متن زیر را ناشناس کنید. قوانین:
341
- 1. اسامی اشخاص → person-01, person-02, ...
342
- 2. نام شرکت‌ها/سازمان‌ها → company-01, company-02, ...
343
- 3. مقادیر پولی → amount-01, amount-02, ...
344
- 4. ��رصدها → percent-01, percent-02, ...
345
- 5. فقط این توکن‌ها استفاده کنید
346
- 6. شماره‌های نسخه را درست حفظ کنید
347
- 7. اگر موجودیت تکرار شود از شماره قدیمی استفاده کنید
348
-
349
- متن:
350
- {text}
351
-
352
- خروجی: فقط متن ناشناس شده"""
353
-
354
- response1 = requests.post(
355
- "https://api.cerebras.ai/v1/chat/completions",
356
- headers={
357
- "Authorization": f"Bearer {self.cerebras_key}",
358
- "Content-Type": "application/json"
359
- },
360
- json={
361
- "model": "llama-3.3-70b",
362
- "messages": [{"role": "user", "content": prompt1}],
363
- "max_tokens": 4096,
364
- "temperature": 0.1
365
- },
366
- timeout=60
367
- )
368
-
369
- if response1.status_code != 200:
370
- logger.error(f"❌ Cerebras Error: {response1.status_code}")
371
- raise Exception(f"Cerebras API Error: {response1.status_code}")
372
-
373
- anonymized_text = response1.json()['choices'][0]['message']['content'].strip()
374
- logger.info("✅ Cerebras: ناشناس‌سازی موفق")
375
-
376
- # مرحله 2: استخراج mapping از مدل
377
- prompt2 = f"""متن اصلی:
378
- {text}
379
-
380
- متن ناشناس شده:
381
- {anonymized_text}
382
-
383
- لطفاً یک جدول mapping برای همه توکن‌های ناشناس ایجاد کن.
384
- برای هر توکن، متن اصلی کامل آن را مشخص کن.
385
-
386
- **مهم:**
387
- - برای person-XX: نام کامل شخص (مثلاً "علی احمدی")
388
- - برای company-XX: نام کامل شرکت/سازمان (مثلاً "شرکت پتروشیمی")
389
- - برای amount-XX: عدد + واحد (مثلاً "80 هزار تومان" یا "50 میلیارد ریال")
390
- - برای percent-XX: عدد + کلمه "درصد" (مثلاً "40 درصد" نه فقط "40")
391
-
392
- خروجی را به این فرمت JSON بده (فقط JSON، بدون توضیح اضافی):
393
- {{
394
- "person-01": "متن اصلی کامل",
395
- "company-01": "متن اصلی کامل",
396
- "amount-01": "متن اصلی کامل با واحد",
397
- "percent-01": "عدد + درصد",
398
- ...
399
- }}"""
400
-
401
- response2 = requests.post(
402
- "https://api.cerebras.ai/v1/chat/completions",
403
- headers={
404
- "Authorization": f"Bearer {self.cerebras_key}",
405
- "Content-Type": "application/json"
406
- },
407
- json={
408
- "model": "llama-3.3-70b",
409
- "messages": [{"role": "user", "content": prompt2}],
410
- "max_tokens": 2048,
411
- "temperature": 0.1
412
- },
413
- timeout=60
414
- )
415
-
416
- if response2.status_code == 200:
417
- mapping_text = response2.json()['choices'][0]['message']['content'].strip()
418
- mapping_text = mapping_text.replace('```json', '').replace('```', '').strip()
419
-
420
- try:
421
- self.mapping_table = json.loads(mapping_text)
422
- self._fix_percent_mapping()
423
- self.reverse_mapping = {v: k for k, v in self.mapping_table.items()}
424
- logger.info(f"✅ Mapping استخراج شد: {len(self.mapping_table)} موجودیت")
425
- except json.JSONDecodeError:
426
- logger.warning("⚠️ خطا در parse کردن JSON mapping - استفاده از روش fallback")
427
- self._extract_mapping_from_text(text, anonymized_text)
428
- else:
429
- logger.warning("⚠️ خطا در دریافت mapping - استفاده از روش fallback")
430
- self._extract_mapping_from_text(text, anonymized_text)
431
-
432
- return anonymized_text, self.mapping_table
433
-
434
- except Exception as e:
435
- logger.error(f"❌ Cerebras Exception: {e}")
436
- raise
437
-
438
- def _fix_percent_mapping(self):
439
- """اصلاح mapping برای درصدها و مقادیر"""
440
- for token, value in self.mapping_table.items():
441
- value_str = str(value).strip()
442
-
443
- if token.startswith('percent-'):
444
- if not re.search(r'(درصد|%|درصدی)', value_str):
445
- self.mapping_table[token] = f"{value_str} درصد"
446
- logger.info(f"✅ اصلاح {token}: '{value_str}' → '{value_str} درصد'")
447
-
448
- elif token.startswith('amount-'):
449
- if not re.search(r'(میلیارد|میلیون|هزار|تومان|ریال|دلار|یورو|تن)', value_str):
450
- logger.warning(f"⚠️ {token}: فقط عدد '{value_str}' - واحد مشخص نیست")
451
-
452
- def _extract_mapping_from_text(self, original: str, anonymized: str):
453
- """استخراج mapping از متن‌های اصلی و ناشناس شده"""
454
- all_tokens = []
455
- for entity_type in ['person', 'company', 'amount', 'percent']:
456
- tokens = re.findall(f'{entity_type}-\\d+', anonymized)
457
- all_tokens.extend([(t, entity_type) for t in tokens])
458
-
459
- all_tokens = sorted(set(all_tokens), key=lambda x: (x[1], int(x[0].split('-')[1])))
460
-
461
- patterns = {
462
- 'person': r'\b[ء-ي]+\s+[ء-ي]+(?:\s+[ء-ي]+)*\b',
463
- 'company': r'(?:شرکت|بانک|سازمان|گروه|هلدینگ)\s+[ء-ي]+(?:\s+[ء-ي]+)*',
464
- 'amount': r'\d+(?:\.\d+)?\s*(?:میلیارد|میلیون|هزار|تومان|ریال|دلار|یورو|تن)',
465
- 'percent': r'\d+(?:\.\d+)?\s*(?:درصد|%|درصدی)',
466
- }
467
-
468
- original_entities = {}
469
- for entity_type, pattern in patterns.items():
470
- matches = list(re.finditer(pattern, original))
471
- original_entities[entity_type] = [m.group().strip() for m in matches]
472
-
473
- for token, entity_type in all_tokens:
474
- if entity_type in original_entities and original_entities[entity_type]:
475
- token_num = int(token.split('-')[1]) - 1
476
-
477
- if token_num < len(original_entities[entity_type]):
478
- original_text = original_entities[entity_type][token_num]
479
- self.mapping_table[token] = original_text
480
- self.reverse_mapping[original_text] = token
481
- else:
482
- original_text = original_entities[entity_type][-1]
483
- if token not in self.mapping_table:
484
- self.mapping_table[token] = original_text
485
- self.reverse_mapping[original_text] = token
486
-
487
- def analyze_with_model(self, anonymized_text: str, analysis_prompt: str, model_name: str) -> str:
488
- """
489
- اجرای پرامپت‌های درون متن ناشناس‌سازی شده با مدل انتخابی
490
- """
491
- logger.info(f"🤖 {model_name} اجرای پرامپت...")
492
-
493
- if not analysis_prompt or not analysis_prompt.strip():
494
- logger.info("⚠️ پرامپتی وارد نشده - متن ناشناس‌سازی شده برگردانده می‌شود")
495
- return anonymized_text
496
-
497
- try:
498
- # ساخت system message
499
- system_msg = """شما یک تحلیلگر مالی حرفه‌ای هستید. متن حاوی کدهای ناشناس است (person-XX، company-XX، amount-XX، percent-XX).
500
- به سوالات و درخواست‌ها با دقت پاسخ دهید و این کدها را در پاسخ خود حفظ کنید."""
501
-
502
- # ساخت پیام کامل
503
- full_text = f"""{analysis_prompt}
504
-
505
- متن برای تحلیل:
506
- {anonymized_text}"""
507
-
508
- # ارسال به مدل انتخابی
509
- response = self.model_sender.send_message(full_text, model_name, system_msg)
510
-
511
- logger.info(f"✅ {model_name} پاسخ داد: {len(response)} کاراکتر")
512
- return response
513
-
514
- except Exception as e:
515
- logger.error(f"❌ {model_name} Exception: {e}")
516
- return f"❌ خطا در {model_name}: {str(e)}"
517
-
518
- def restore_text(self, anonymized_text: str) -> str:
519
- """بازگردانی متن ناشناس‌سازی شده به متن اصلی"""
520
- logger.info("🔄 بازگردانی متن...")
521
-
522
- if not self.mapping_table:
523
- logger.warning("⚠️ جدول نگاشت خالی است")
524
- return anonymized_text
525
-
526
- restored = anonymized_text
527
- for placeholder, original in sorted(self.mapping_table.items()):
528
- restored = restored.replace(placeholder, original)
529
-
530
- logger.info("✅ بازگردانی کامل")
531
- return restored
532
-
533
- def get_mapping_table_md(self) -> str:
534
- """تبدیل جدول نگاشت به Markdown"""
535
- if not self.mapping_table:
536
- return "### 📋 جدول نگاشت\n\nهیچ موجودیتی شناسایی نشد"
537
-
538
- table = "### 📋 جدول نگاشت\n\n"
539
- table += "| شناسه | متن اصلی |\n"
540
- table += "|-------|----------|\n"
541
-
542
- for token, original in sorted(self.mapping_table.items()):
543
- table += f"| **{token}** | {original} |\n"
544
-
545
- return table
546
-
547
- # متغیر سراسری
548
- anonymizer = None
549
-
550
- def process(input_text: str, analysis_prompt: str, model_choice: str):
551
- """پردازش متن - 4 مرحله"""
552
- global anonymizer
553
-
554
- if not input_text.strip():
555
- return "", "", "", ""
556
-
557
- cerebras_key = os.getenv("CEREBRAS_API_KEY")
558
-
559
- if not anonymizer:
560
- anonymizer = AnonymizerAdvanced(cerebras_key)
561
- else:
562
- anonymizer.mapping_table = {}
563
- anonymizer.reverse_mapping = {}
564
-
565
- try:
566
- logger.info("=" * 70)
567
- logger.info(f"🚀 شروع پردازش - مدل تحلیل: {model_choice}")
568
- logger.info("=" * 70)
569
-
570
- # مرحله 1: ناشناس‌سازی
571
- logger.info("📝 مرحله 1: ناشناس‌سازی...")
572
- anonymized_text, _ = anonymizer.anonymize_with_cerebras(input_text)
573
- logger.info(f"✅ ناشناس‌سازی: {len(anonymized_text)} کاراکتر")
574
-
575
- # مرحله 2: مدل انتخابی
576
- logger.info(f"🤖 مرحله 2: {model_choice}...")
577
- model_response = anonymizer.analyze_with_model(anonymized_text, analysis_prompt, model_choice)
578
- logger.info(f"✅ {model_choice}: {len(model_response)} کاراکتر")
579
-
580
- # مرحله 3: بازگردانی
581
- logger.info("🔄 مرحله 3: بازگردانی...")
582
- restored_text = anonymizer.restore_text(model_response)
583
- logger.info("✅ بازگردانی کامل")
584
-
585
- # مرحله 4: جدول نگاشت
586
- logger.info("📋 مرحله 4: جدول نگاشت...")
587
- mapping_str = anonymizer.get_mapping_table_md()
588
- logger.info(f"✅ {len(anonymizer.mapping_table)} موجودیت")
589
-
590
- logger.info("=" * 70)
591
- logger.info("✅ تمام مراحل کامل!")
592
- logger.info("=" * 70)
593
-
594
- return restored_text, model_response, anonymized_text, mapping_str
595
-
596
- except Exception as e:
597
- logger.error(f"❌ خطا: {str(e)}", exc_info=True)
598
- return "", f"❌ خطا: {str(e)}", "", ""
599
-
600
- def clear_all():
601
- """پاک کردن همه"""
602
- return "", "", "", "", "", ""
603
-
604
- # Gradio Interface
605
- css_rtl = """
606
- .input-box { direction: rtl; text-align: right; }
607
- .textbox textarea { direction: rtl; text-align: right; font-family: 'Tahoma', serif; }
608
- """
609
-
610
- with gr.Blocks(title="سیستم ناشناس‌سازی متون", theme=gr.themes.Soft(), css=css_rtl) as app:
611
-
612
- gr.Markdown("# 🔐 سیستم ناشناس‌سازی متون مالی فارسی (چند مدل)", elem_classes="input-box")
613
- gr.Markdown("### با قابلیت Retry و مدیریت خطای پیشرفته", elem_classes="input-box")
614
-
615
- with gr.Row():
616
- with gr.Column(scale=1):
617
- # منوی انتخاب مدل
618
- model_dropdown = gr.Dropdown(
619
- choices=["ChatGPT-4o-mini", "Grok", "Gemini", "DeepSeek"],
620
- value="ChatGPT-4o-mini",
621
- label="🤖 انتخاب مدل تحلیل",
622
- info="هر مدل دارای Retry و Error Handling است",
623
- interactive=True
624
- )
625
-
626
- analysis_prompt = gr.Textbox(
627
- lines=8,
628
- placeholder="مثال: این متن را خلاصه کن\nمثال: نقاط قوت و ضعف را استخراج کن",
629
- label="📋 دستورات تحلیل (اختیاری)",
630
- elem_classes="textbox"
631
- )
632
-
633
- gr.Markdown("---")
634
-
635
- with gr.Column():
636
- process_btn = gr.Button(
637
- "▶️ پردازش",
638
- variant="primary",
639
- size="lg"
640
- )
641
-
642
- clear_btn = gr.Button(
643
- "🗑️ پاک کردن",
644
- variant="stop",
645
- size="lg"
646
- )
647
-
648
- with gr.Column(scale=3):
649
- input_text = gr.Textbox(
650
- lines=14,
651
- placeholder="متن مالی/خبری فارسی را وارد کنید...",
652
- label="📝 متن ورودی",
653
- elem_classes="textbox"
654
- )
655
-
656
- gr.Markdown("---")
657
- gr.Markdown("## 📊 نتایج پردازش", elem_classes="input-box")
658
-
659
- with gr.Row():
660
- with gr.Column(scale=1):
661
- restored_text = gr.Textbox(
662
- lines=12,
663
- label="✅ متن بازگردانی شده",
664
- interactive=False,
665
- elem_classes="textbox"
666
- )
667
-
668
- with gr.Column(scale=1):
669
- model_analysis = gr.Textbox(
670
- lines=12,
671
- label="🤖 تحلیل مدل (ناشناس)",
672
- interactive=False,
673
- elem_classes="textbox"
674
- )
675
-
676
- with gr.Column(scale=1):
677
- anonymized_text = gr.Textbox(
678
- lines=12,
679
- label="🔒 متن ناشناس‌شده",
680
- interactive=False,
681
- elem_classes="textbox"
682
- )
683
-
684
- gr.Markdown("---")
685
-
686
- mapping_table = gr.Markdown(
687
- value="### 📋 جدول نگاشت\n\nهنوز پردازشی انجام نشده",
688
- label="📋 جدول نگاشت",
689
- elem_classes="input-box"
690
- )
691
-
692
- # Event Handlers
693
- process_btn.click(
694
- fn=process,
695
- inputs=[input_text, analysis_prompt, model_dropdown],
696
- outputs=[restored_text, model_analysis, anonymized_text, mapping_table]
697
- )
698
-
699
- clear_btn.click(
700
- fn=clear_all,
701
- outputs=[input_text, analysis_prompt, restored_text, model_analysis, anonymized_text, mapping_table]
702
- )
703
-
704
- if __name__ == "__main__":
705
- print("=" * 70)
706
- print("🚀 سیستم ناشناس‌سازی متون در حال راه‌اندازی...")
707
- print("=" * 70)
708
- print("\n📋 API Keys مورد نیاز:\n")
709
- print(" ✅ CEREBRAS_API_KEY (برای ناشناس‌سازی)")
710
- print(" ✅ OPENAI_API_KEY (برای ChatGPT)")
711
- print(" ✅ XAI_API_KEY (برای Grok)")
712
- print(" ✅ GEMINI_API_KEY (برای Gemini)")
713
- print(" ✅ DEEPSEEK_API_KEY (برای DeepSeek)")
714
- print("\n⚡ ویژگی‌های جدید:\n")
715
- print(" 🔄 Retry mechanism برای همه مدل‌ها")
716
- print(" ⚠️ مدیریت خطاهای Rate limiting")
717
- print(" 🛡️ Error handling پیشرفته")
718
- print(" ⏱️ Timeout handling با تلاش مجدد")
719
- print("\n🎯 روش ناشناس‌سازی: Cerebras (Llama 3.3-70B)")
720
- print("🤖 مدل‌های تحلیل: ChatGPT, Grok, Gemini, DeepSeek")
721
- print("=" * 70 + "\n")
722
-
723
- app.launch(
724
- server_name="0.0.0.0",
725
- server_port=7860,
726
- share=False,
727
- show_error=True
728
- )