leilaghomashchi commited on
Commit
7fbe3d9
·
verified ·
1 Parent(s): 2a8ee29

Upload llma3_3-70b_with_chatgpt_fixed (3).py

Browse files
Files changed (1) hide show
  1. llma3_3-70b_with_chatgpt_fixed (3).py +427 -0
llma3_3-70b_with_chatgpt_fixed (3).py ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import requests
5
+ import json
6
+ import gradio as gr
7
+ import logging
8
+ from typing import Dict, Any, Tuple
9
+ import os
10
+ from dataclasses import dataclass
11
+ import re
12
+ from difflib import SequenceMatcher
13
+
14
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ @dataclass
19
+ class CerebrasConfig:
20
+ api_key: str
21
+ base_url: str = "https://api.cerebras.ai/v1"
22
+ model: str = "llama-3.3-70b"
23
+ max_tokens: int = 2000
24
+ temperature: float = 0.1
25
+
26
+
27
+ class AdvancedCerebrasAnonymizer:
28
+
29
+ def __init__(self, api_key: str = None, openai_api_key: str = None):
30
+ if api_key is None:
31
+ api_key = os.getenv("CEREBRAS_API_KEY")
32
+ if not api_key:
33
+ raise ValueError("❌ کلید API Cerebras یافت نشد")
34
+
35
+ self.config = CerebrasConfig(api_key=api_key)
36
+ self.openai_api_key = openai_api_key or os.getenv("OPENAI_API_KEY", "")
37
+ self.system_prompt = self._create_system_prompt()
38
+ self.mapping_table = {}
39
+ logger.info("✅ سیستم آماده شد")
40
+
41
+ def _create_system_prompt(self) -> str:
42
+ return """شما یک «ناشناس‌ساز متون» هستید. وظیفه‌تان جایگزینی اسامی خاص و مقادیر عددی با شناسه‌های استاندارد است.
43
+
44
+ قوانین:
45
+ - شرکت‌ها: company-01, company-02, ...
46
+ - اشخاص: person-01, person-02, ...
47
+ - اعداد/مقادیر: amount-01, amount-02, ...
48
+ - درصدها: percent-01, percent-02, ...
49
+
50
+ مثال:
51
+ - "ایرانخودرو" → "company-01"
52
+ - "۴.۵۸" → "percent-01"
53
+ - "۳۷ میلیارد" → "amount-01"
54
+
55
+ فقط متن ناشناس‌شده را برگردانید. هیچ توضیح اضافی نیاید."""
56
+
57
+ def anonymize_text(self, text: str) -> Dict[str, Any]:
58
+ try:
59
+ if not text or not text.strip():
60
+ return {"success": False, "error": "متن ورودی خالی است"}
61
+
62
+ logger.info(f"🔄 ناشناس‌سازی... ({len(text)} کاراکتر)")
63
+
64
+ headers = {
65
+ "Authorization": f"Bearer {self.config.api_key}",
66
+ "Content-Type": "application/json"
67
+ }
68
+
69
+ payload = {
70
+ "model": self.config.model,
71
+ "messages": [
72
+ {"role": "system", "content": self.system_prompt},
73
+ {"role": "user", "content": f"لطفاً این متن را ناشناس‌سازی کنید:\n\n{text}"}
74
+ ],
75
+ "max_tokens": self.config.max_tokens,
76
+ "temperature": self.config.temperature
77
+ }
78
+
79
+ response = requests.post(
80
+ f"{self.config.base_url}/chat/completions",
81
+ headers=headers,
82
+ json=payload,
83
+ timeout=60
84
+ )
85
+
86
+ if response.status_code != 200:
87
+ return {"success": False, "error": f"خطای API: {response.status_code}"}
88
+
89
+ result = response.json()
90
+ anonymized_text = result["choices"][0]["message"]["content"]
91
+ usage = result.get("usage", {})
92
+
93
+ self._extract_mapping_advanced(text, anonymized_text)
94
+
95
+ logger.info(f"✅ ناشناس‌سازی موفق ({len(self.mapping_table)} موجودیت)")
96
+
97
+ return {
98
+ "success": True,
99
+ "anonymized_text": anonymized_text,
100
+ "usage": usage
101
+ }
102
+
103
+ except Exception as e:
104
+ logger.error(f"❌ خطا: {str(e)}")
105
+ return {"success": False, "error": str(e)}
106
+
107
+ def _extract_mapping_advanced(self, original: str, anonymized: str):
108
+ """استخراج mapping دقیق با مقایسه متن‌ها"""
109
+ self.mapping_table = {}
110
+
111
+ try:
112
+ # تقسیم به جملات
113
+ orig_lines = re.split(r'([.!?])', original)
114
+ anon_lines = re.split(r'([.!?])', anonymized)
115
+
116
+ for orig_line, anon_line in zip(orig_lines, anon_lines):
117
+ if not orig_line.strip():
118
+ continue
119
+
120
+ # تقسیم به کلمات
121
+ orig_words = orig_line.split()
122
+ anon_words = anon_line.split()
123
+
124
+ # یافتن تطابقات
125
+ i = 0
126
+ j = 0
127
+
128
+ while i < len(orig_words) and j < len(anon_words):
129
+ orig_word = orig_words[i]
130
+ anon_word = anon_words[j]
131
+
132
+ # اگر کدی است
133
+ if re.match(r'(company|person|amount|percent)-\d+', anon_word):
134
+ # سعی کن تا 3 کلمه قبل ��ا پیدا کن
135
+ found = False
136
+
137
+ for phrase_len in range(min(4, len(orig_words) - i), 0, -1):
138
+ phrase = ' '.join(orig_words[i:i+phrase_len])
139
+
140
+ # بررسی اینکه آیا این عبارت منطقی است
141
+ if self._is_valid_entity(phrase, anon_word):
142
+ if phrase not in self.mapping_table:
143
+ self.mapping_table[phrase] = anon_word
144
+ logger.info(f" {phrase} → {anon_word}")
145
+ i += phrase_len
146
+ found = True
147
+ break
148
+
149
+ if not found:
150
+ i += 1
151
+ j += 1
152
+ else:
153
+ i += 1
154
+ j += 1
155
+
156
+ except Exception as e:
157
+ logger.warning(f"⚠️ خطا در mapping: {str(e)}")
158
+
159
+ def _is_valid_entity(self, phrase: str, code: str) -> bool:
160
+ """بررسی صحت موجودیت"""
161
+ phrase_lower = phrase.lower()
162
+
163
+ if code.startswith('company'):
164
+ return any(word in phrase_lower for word in ['شرکت', 'بانک', 'سازمان', 'وزارت', 'گروه'])
165
+ elif code.startswith('person'):
166
+ return len(phrase) > 2
167
+ elif code.startswith('amount'):
168
+ return bool(re.search(r'[\d]+', phrase))
169
+ elif code.startswith('percent'):
170
+ return 'درصد' in phrase_lower or '%' in phrase
171
+
172
+ return False
173
+
174
+ def send_to_chatgpt(self, anonymized_text: str) -> Dict[str, Any]:
175
+ try:
176
+ if not anonymized_text or not anonymized_text.strip():
177
+ return {"success": False, "error": "متن ناشناس‌شده خالی است"}
178
+
179
+ if not self.openai_api_key:
180
+ return {"success": False, "error": "کلید OpenAI تنظیم نشده"}
181
+
182
+ logger.info("🤖 ارسال به ChatGPT...")
183
+
184
+ headers = {
185
+ "Authorization": f"Bearer {self.openai_api_key}",
186
+ "Content-Type": "application/json"
187
+ }
188
+
189
+ data = {
190
+ "model": "gpt-4o-mini",
191
+ "messages": [
192
+ {"role": "system", "content": "شما یک تحلیلگر مالی حرفه‌ای هستید."},
193
+ {"role": "user", "content": anonymized_text}
194
+ ],
195
+ "max_tokens": 2000,
196
+ "temperature": 0.7
197
+ }
198
+
199
+ response = requests.post(
200
+ "https://api.openai.com/v1/chat/completions",
201
+ headers=headers,
202
+ json=data,
203
+ timeout=30
204
+ )
205
+
206
+ if response.status_code == 200:
207
+ result = response.json()
208
+ return {"success": True, "response": result['choices'][0]['message']['content']}
209
+ else:
210
+ error_data = response.json() if response.content else {}
211
+ error_message = error_data.get('error', {}).get('message', response.text)
212
+ return {"success": False, "error": error_message}
213
+
214
+ except Exception as e:
215
+ logger.error(f"❌ خطا: {str(e)}")
216
+ return {"success": False, "error": str(e)}
217
+
218
+ def _create_reverse_mapping(self, mapping_table: Dict[str, str]) -> Dict[str, str]:
219
+ """ایجاد reverse mapping: code -> original"""
220
+ reverse_map = {}
221
+ for original, code in mapping_table.items():
222
+ reverse_map[code] = original
223
+ return reverse_map
224
+
225
+ def deanonymize_response(self, gpt_response: str, mapping_table: Dict[str, str]) -> str:
226
+ """بازگردانی تمام موجودیت‌ها (placeholder‌ها و کد‌های ساده را پشتیبانی می‌کند)"""
227
+ try:
228
+ if not mapping_table:
229
+ return gpt_response
230
+
231
+ result = gpt_response
232
+
233
+ # ایجاد reverse mapping برای تبدیل code به original
234
+ reverse_map = self._create_reverse_mapping(mapping_table)
235
+
236
+ # مرتب‌سازی بر اساس طول (طولانی‌ترین اول)
237
+ sorted_reverse = sorted(reverse_map.items(), key=lambda x: len(x[0]), reverse=True)
238
+
239
+ logger.info(f"🔄 شروع بازگردانی {len(sorted_reverse)} موجودیت")
240
+
241
+ for code, original in sorted_reverse:
242
+ # الگوهای مختلف برای جستجو:
243
+ # 1. [code] (داخل براکت)
244
+ pattern_bracket = f"[{code}]"
245
+ if pattern_bracket in result:
246
+ result = result.replace(pattern_bracket, original)
247
+ logger.info(f" [{code}] → {original} ✓")
248
+
249
+ # 2. code بدون براکت
250
+ if code in result:
251
+ result = result.replace(code, original)
252
+ logger.info(f" {code} → {original} ✓")
253
+
254
+ # جایگزینی نسخه‌های دارای درصد و هزار جدا شده
255
+ result = self._replace_special_patterns(result, reverse_map)
256
+
257
+ logger.info(f"✅ بازگردانی موفق ({len(mapping_table)} موجودیت)")
258
+ return result
259
+
260
+ except Exception as e:
261
+ logger.error(f"❌ خطا در بازگردانی: {str(e)}")
262
+ return gpt_response
263
+
264
+ def _replace_special_patterns(self, text: str, reverse_map: Dict[str, str]) -> str:
265
+ """جایگزینی الگوهای خاص (درصد-XX، amount-XX و غیره) - شامل placeholder‌های بدون براکت"""
266
+ try:
267
+ result = text
268
+
269
+ # الگوی 1: Placeholder‌های با براکت [code]
270
+ pattern_bracket = r'\[((?:company|person|amount|percent|درصد|شرکت|مبلغ)-\d+)\]'
271
+
272
+ def replacer_bracket(match):
273
+ code = match.group(1)
274
+ # تبدیل الگوهای فارسی به انگلیسی
275
+ code_normalized = code.replace('درصد', 'percent').replace('شرکت', 'company').replace('مبلغ', 'amount')
276
+ return reverse_map.get(code_normalized, reverse_map.get(code, match.group(0)))
277
+
278
+ result = re.sub(pattern_bracket, replacer_bracket, result)
279
+ logger.info(f" الگوی 1 (با براکت): جایگزین شد")
280
+
281
+ # الگوی 2: Placeholder‌های داخل پرانتز (code)
282
+ pattern_paren = r'\(((?:company|person|amount|percent|درصد|شرکت|مبلغ)-\d+)\)'
283
+
284
+ def replacer_paren(match):
285
+ code = match.group(1)
286
+ code_normalized = code.replace('درصد', 'percent').replace('شرکت', 'company').replace('مبلغ', 'amount')
287
+ return f"({reverse_map.get(code_normalized, reverse_map.get(code, code))})"
288
+
289
+ result = re.sub(pattern_paren, replacer_paren, result)
290
+ logger.info(f" الگوی 2 (پرانتز): جایگزین شد")
291
+
292
+ # الگوی 3: Placeholder‌های بدون براکت (بسیار مهم!)
293
+ # جایگزینی تمام code-XX که با word boundary احاطه شده‌اند
294
+ pattern_simple = r'\b((?:company|person|amount|percent|درصد|شرکت|مبلغ)-\d+)\b'
295
+
296
+ def replacer_simple(match):
297
+ code = match.group(1)
298
+ code_normalized = code.replace('درصد', 'percent').replace('شرکت', 'company').replace('مبلغ', 'amount')
299
+ replacement = reverse_map.get(code_normalized, reverse_map.get(code, None))
300
+ if replacement:
301
+ logger.info(f" {code} → {replacement}")
302
+ return replacement
303
+ return match.group(0)
304
+
305
+ result = re.sub(pattern_simple, replacer_simple, result)
306
+ logger.info(f" الگوی 3 (بدون براکت): جایگزین شد")
307
+
308
+ return result
309
+
310
+ except Exception as e:
311
+ logger.warning(f"⚠️ خطا در جایگزینی الگوهای خاص: {str(e)}")
312
+ return text
313
+
314
+
315
+ def create_interface():
316
+ try:
317
+ anonymizer = AdvancedCerebrasAnonymizer()
318
+ except ValueError as e:
319
+ return gr.Interface(fn=lambda x: str(e), inputs="textbox", outputs="textbox", title="❌ خطا")
320
+
321
+ def process_text(input_text: str) -> Tuple[str, str, str, str]:
322
+ logger.info("=" * 60)
323
+ logger.info("شروع پردازش")
324
+ logger.info("=" * 60)
325
+
326
+ if not input_text.strip():
327
+ return "❌ متن ورودی خالی است", "", "", ""
328
+
329
+ try:
330
+ # مرحله 1: ناشناس‌سازی
331
+ logger.info("1️⃣ ناشناس‌سازی...")
332
+ anon_result = anonymizer.anonymize_text(input_text)
333
+
334
+ if not anon_result["success"]:
335
+ return f"❌ {anon_result['error']}", "", "", ""
336
+
337
+ anonymized_text = anon_result["anonymized_text"]
338
+ usage_info = anon_result.get("usage", {})
339
+
340
+ # مرحله 2: ارسال به ChatGPT
341
+ logger.info("2️⃣ ارسال به ChatGPT...")
342
+ gpt_result = anonymizer.send_to_chatgpt(anonymized_text)
343
+
344
+ # تعریف اولیه
345
+ gpt_response = ""
346
+ gpt_response_deanon = ""
347
+
348
+ if not gpt_result["success"]:
349
+ gpt_response = f"❌ {gpt_result['error']}"
350
+ gpt_response_deanon = ""
351
+ else:
352
+ # پاسخ ChatGPT (ناشناس)
353
+ gpt_response = gpt_result["response"]
354
+
355
+ # مرحله 3: بازگردانی
356
+ logger.info("3️⃣ بازگردانی...")
357
+ gpt_response_deanon = anonymizer.deanonymize_response(gpt_response, anonymizer.mapping_table)
358
+
359
+ # آمار
360
+ stats = f"""Token: {usage_info.get('total_tokens', '?')} | Mapping: {len(anonymizer.mapping_table)}"""
361
+
362
+ logger.info("=" * 60)
363
+ logger.info("✅ پردازش کامل")
364
+ logger.info("=" * 60)
365
+
366
+ # ✅ ترتیب صحیح:
367
+ # 1. آمار
368
+ # 2. متن ناشناس‌شده (ورودی)
369
+ # 3. پاسخ ChatGPT (ناشناس) ← باکس "🤖 نتایج ChatGPT"
370
+ # 4. نتیجه نهایی (بازگردانی شده) ← باکس "✅ نتیجه نهایی"
371
+ return stats, anonymized_text, gpt_response, gpt_response_deanon
372
+
373
+ except Exception as e:
374
+ return f"❌ خطا: {str(e)}", "", "", ""
375
+
376
+ def copy_text(text: str):
377
+ if not text or not text.strip():
378
+ return gr.update(visible=False), "⚠️ متنی وجود ندارد"
379
+ return gr.update(value=text, visible=True), "✅ آماده برای کپی"
380
+
381
+ def clear_all():
382
+ anonymizer.mapping_table = {}
383
+ return "", "", "", "", gr.update(visible=False)
384
+
385
+ with gr.Blocks(title="سیستم ناشناس‌سازی", theme=gr.themes.Soft()) as interface:
386
+ gr.HTML("<h1 style='text-align: center; color: #FFD700;'>🔐 سیستم ناشناس‌سازی</h1>")
387
+
388
+ with gr.Row():
389
+ # ستون 1
390
+ with gr.Column(scale=1):
391
+ gr.HTML("<h2>📥 ورودی</h2>")
392
+ input_text = gr.Textbox(lines=20, placeholder="متن را وارد کنید...", label="", rtl=True)
393
+
394
+ process_btn = gr.Button("🚀 پردازش", variant="primary", size="lg")
395
+ with gr.Row():
396
+ copy_btn = gr.Button("📋 کپی", scale=1)
397
+ clear_btn = gr.Button("🗑️ پاک", variant="stop", scale=1)
398
+
399
+ copy_output = gr.Textbox(visible=False)
400
+
401
+ # ستون 2
402
+ with gr.Column(scale=1):
403
+ gr.HTML("<h2>🎭 متن ناشناس‌شده</h2>")
404
+ anonymized_output = gr.Textbox(lines=20, placeholder="", label="", interactive=False, rtl=True)
405
+
406
+ # ستون 3
407
+ with gr.Column(scale=1):
408
+ gr.HTML("<h2>🤖 نتایج ChatGPT</h2>")
409
+ gpt_output = gr.Textbox(lines=10, placeholder="", label="📤 پاسخ", interactive=False, rtl=True)
410
+ final_output = gr.Textbox(lines=10, placeholder="", label="✅ نتیجه نهایی", interactive=False, rtl=True)
411
+ statistics_output = gr.Textbox(lines=1, label="📊 آمار", interactive=False)
412
+
413
+ process_btn.click(fn=process_text, inputs=[input_text], outputs=[statistics_output, anonymized_output, gpt_output, final_output])
414
+ copy_btn.click(fn=copy_text, inputs=[final_output], outputs=[copy_output, statistics_output])
415
+ clear_btn.click(fn=clear_all, outputs=[input_text, anonymized_output, gpt_output, final_output, copy_output])
416
+
417
+ return interface
418
+
419
+
420
+ def main():
421
+ print("\n🔐 سیستم ناشناس‌سازی - نسخه 1.0.0\n")
422
+ interface = create_interface()
423
+ interface.launch(server_name="0.0.0.0", server_port=7860, share=False, show_error=True)
424
+
425
+
426
+ if __name__ == "__main__":
427
+ main()