leilaghomashchi commited on
Commit
f0a9460
·
verified ·
1 Parent(s): ddfff48

Upload llma3_3-70b_with_chatgpt_fixed (1).py

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