KashefTech commited on
Commit
9501840
·
verified ·
1 Parent(s): d60a141

Delete app.py

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