leilaghomashchi commited on
Commit
6818df6
·
verified ·
1 Parent(s): 683d904

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -687
app.py DELETED
@@ -1,687 +0,0 @@
1
- import gradio as gr
2
- import re
3
- import os
4
- import requests
5
- import logging
6
- from typing import Dict, List, Tuple, Set
7
- import json
8
-
9
- logging.basicConfig(level=logging.INFO)
10
- logger = logging.getLogger(__name__)
11
-
12
- class AnonymizerCerebrasEnhanced:
13
- def __init__(self, api_key: str = None):
14
- self.api_key = api_key or os.getenv("CEREBRAS_API_KEY")
15
- self.mapping_table = {}
16
- self.counters = {
17
- 'company': 0, 'person': 0, 'amount': 0, 'phone': 0,
18
- 'email': 0, 'id_number': 0, 'date': 0, 'location': 0,
19
- 'percent': 0
20
- }
21
- self.seen_entities = {} # برای ثبات نگاشت
22
-
23
- if not self.api_key:
24
- raise ValueError("❌ کلید API Cerebras یافت نشد!")
25
-
26
- logger.info("✅ Anonymizer Enhanced مقداردهی شد")
27
-
28
- def get_system_prompt(self) -> str:
29
- """ایجاد دستورالعمل سیستمی پیشرفته برای Groq"""
30
- return """شما یک «ناشناس‌ساز متون مالی/خبری فارسی» هستید. وظیفه‌تان جایگزینی اسامی خاص و مقادیر عددی با شناسه‌های بی‌معناست.
31
-
32
- ## **قوانین اندیس‌گذاری - CRITICAL**
33
- ### **1. ترتیب شماره‌گذاری الزامی:**
34
- - شرکت‌ها: company-01, company-02, company-03, company-04, ... (پیوسته و بدون گپ)
35
- - اشخاص: person-01, person-02, person-03, ... (پیوسته و بدون گپ)
36
- - اعداد: amount-01, amount-02, amount-03, ... (پیوسته و بدون گپ)
37
- - درصدها: percent-01, percent-02, percent-03, ... (پیوسته و بدون گپ)
38
- - تاریخها: date-01, date-02, date-03, ... (پیوسته و بدون گپ)
39
-
40
- ### **2. ثبات شناسه‌ها در متن - MUST MAINTAIN:**
41
- - اگر "همراه اول" اول‌بار company-01 شد، در تمام متن همان باشد
42
- - اگر "مهدی احمدی" اول‌بار person-01 شد، در تمام متن همان باشد
43
- - **CRITICAL: اگر "سروش خسروی" = person-01، تو "خسروی" تنهایی هم = person-01 باشد**
44
-
45
- ### **3. تشخیص صحیح انواع:**
46
- **شرکت/سازمان:** همراه اول، بانک ملی، ایران‌خودرو، سایپا، بانک مرکزی، سامانه کدال، وزارت نفت
47
- **شخص:** مهدی اخوان بهابادی، محمدرضا فرزین، ابوالفضل نجارزاده، سروش خسروی
48
- **عدد:** 37، 70، 677، 73.7، 178 (هر عددی)
49
- **درصد:** 37 درصدی، 15 درصدی، 53 درصد، 43%
50
- **تاریخ:** 1403، 1404، اردیبهشت، فروردین، 30 آذر 1403
51
-
52
- ## **انواع موجودیت‌ها:**
53
-
54
- **company-XX:** نام شرکت‌ها، سازمان‌ها، بانک‌ها، هلدینگ‌ها، گروه‌های مالی
55
-
56
- **person-XX:** نام و نام خانوادگی اشخاص - شامل نام کامل، نام کوچک تنهایی، نام خانوادگی تنهایی
57
-
58
- **amount-XX:** مبالغ مالی شامل ریال، تومان، همت، دلار، تن، دستگاه و واحدهای اندازه‌گیری
59
-
60
- **percent-XX:** درصدها و نسبت‌ها
61
-
62
- **date-XX:** تمام تاریخ‌ها شامل سال، ماہ، روز و ترکیب آنها
63
-
64
- ## **قوانین کلیدی:**
65
-
66
- 1. **ترتیب شماره‌گذاری:** اولین باری که موجودیت ظاهر می‌شود، شماره می‌گیرد (01، 02، 03، ...)
67
-
68
- 2. **حفظ هویت یکسان:** اگر همان موجودیت دوباره آمد، از همان شماره استفاده کن.
69
-
70
- 3. **CRITICAL - Entity Linking برای اشخاص:**
71
- - اگر "سروش خسروی" = person-01 شد، تو "خسروی" تنهایی = person-01
72
- - اگر "محمدرضا فرزین" = person-01 شد، تو "فرزین" یا "محمدرضا" = person-01
73
- - اگر "علی احمدی" = person-01 شد، تو "احمدی"، "علی"، "آن شخص" همه = person-01
74
- - **MUST TRACK: نام کامل → نام کوچک → نام خانوادگی → ضمیرها**
75
- - **نام خانوادگی تنهایی را هرگز بدون linking رها نکن**
76
-
77
- 4. **تشخیص نام‌های مختلف:** "فولاد مبارکه اصفهان" و "فولاد مبارکه" و "این شرکت" همه company-01 هستند.
78
-
79
- 5. **CRITICAL - تمام تاریخ‌ها باید Anonymize شوند:**
80
- - سال ONLY: "سال 1403" → "سال date-01"
81
- - ماہ ONLY: "اردیبهشت" → "date-02"
82
- - سال + ماہ: "اردیبهشت 1404" → "date-03 date-04"
83
- - تاریخ مکمل: "1403/04/12" → "date-05/date-06/date-07"
84
- - **NO EXCEPTION: تمام اعداد تاریخ باید anonymize شوند**
85
- - **یکسانی برقرار کن: اگر "1403" یک جا date-01 شد، همه جا date-01 باشد**
86
-
87
- 6. **مبالغ و درصدهای مختلف:** هر عدد جدید، شماره جدید می‌گیرد
88
-
89
- 7. **حفظ ساختار:** ��اختار جمله را حفظ کن، کلمات توصیفی مثل "شرکت"، "بانک"، "گروه" را قبل از برچسب حفظ کن
90
-
91
- 8. **هیچ توضیح اضافه‌ای نده:** فقط متن ناشناس‌شده را برگردان
92
-
93
- ## **موارد حفظ شده:**
94
- - عناوین شغلی: مدیرعامل، رئیس کل، مدیرکل، سرپرست
95
- - واحدها: میلیارد تومان، همت، ریال، ماه، سال
96
- - مکان‌ها: تهران، اصفهان، ایران
97
- - کلمات توضیحی: "شرکت"، "بانک"، "گروه"
98
-
99
- ## **ممنوع:**
100
- - کلمات انگلیسی اضافی
101
- - تغییر ساختار جمله
102
- - حذف یا اضافه کردن کلمات
103
- - **نام خانوادگی یا نام کوچک تنهایی را بدون linking رها کردن**
104
-
105
- ## **نمونه‌های آموزشی:**
106
-
107
- **نمونه ۱ - Entity Linking برای نام‌ها (CRITICAL):**
108
- ورودی: سروش خسروی، سرپرست هیأت‌مدیره. خسروی اعلام کرد که سود خالص 216 میلیارد تومان بود. خسروی همچنین به چالش‌ها اشاره کرد.
109
- خروجی: person-01، سرپرست هیأت‌مدیره. person-01 اعلام کرد که سود خالص amount-01 بود. person-01 همچنین به چالش‌ها اشاره کرد.
110
-
111
- **نمونه ۲ - تمام تاریخها Anonymize شوند (CRITICAL):**
112
- ورودی: سال 1403 یکی از سخت‌ترین سال‌ها برای صنعت پتروشیمی بود. در اردیبهشت 1404 شرکت گزارش منتشر کرد و کاهش سود خالص در سال 1403 را اعلام کرد.
113
- خروجی: سال date-01 یکی از سخت‌ترین سال‌ها برای صنعت پتروشیمی بود. در date-02 date-03 شرکت گزارش منتشر کرد و کاهش سود خالص در سال date-01 را اعلام کرد.
114
-
115
- **نمونه ۳:**
116
- ورودی: مهدی اخوان بهابادی، مدیرعامل همراه اول، اعلام کرد درآمد عملیاتی شرکت با رشد 37 درصدی به 70 هزار و 677 میلیارد تومان رسیده است. سود خالص 7101 میلیارد تومان و تلفیقی گروه همراه اول 8003 میلیارد تومان شد.
117
- خروجی: person-01، مدیرعامل company-01، اعلام کرد درآمد عملیاتی شرکت با رشد percent-01 به amount-01 رسیده است. سود خالص amount-02 و تلفیقی گروه company-01 amount-03 شد.
118
-
119
- **نمونه ۴:**
120
- ورودی: بانک مرکزی و بانک ملی با همکاری محمدرضا فرزین، 60 درصد سپرده‌ها را مدیریت کردند.
121
- خروجی: company-01 و company-02 با همکاری person-01، percent-01 سپرده‌ها را مدیریت کردند.
122
-
123
- **نمونه ۵:**
124
- ورودی: سایپا و ایران‌خودرو مجموع زیان 620 همت داشتند و سایپا 269 هزار میلیارد زیان اعلام کرد.
125
- خروجی: company-01 و company-02 مجموع زیان amount-01 داشتند و company-01 amount-02 زیان اعلام کرد.
126
-
127
- **نمونه ۶ - تاریخ مکمل:**
128
- ورودی: مجمع عمومی مورخ 1403/04/12 برگزار شد و گزارش مالی منتهی به 30 آذر 1403 تصویب رسید.
129
- خروجی: مجمع عمومی مورخ date-01/date-02/date-03 برگزار شد و گزارش مالی منتهی به date-04 date-05 date-06 تصویب رسید.
130
-
131
- **نمونه ۷:**
132
- ورودی: بانک پاسارگاد با شناسایی سود خالص 155 هزار میلیارد ریالی در رده دوم سودآورترین بانک‌های کشور قرار گرفت. در مقابل، بانک سرمایه با مدیرعاملی فرج‌اله قدمی وضعیت بحرانی دارد.
133
- خروجی: company-01 با شناسایی سود خالص amount-01 در رده دوم سودآورترین بانک‌های کشور قرار گرفت. در مقابل، company-02 با مدیرعاملی person-01 وضعیت بحرانی دارد.
134
-
135
- **نمونه ۸:**
136
- ورودی: بانک سرمایه با مدیرعاملی فرج‌اله قدمی زیان خالص 2700 میلیارد تومانی در سه‌ماهه نخست 1404 گزارش کرد. نسبت کفایت سرمایه به منفی 345 درصد رسیده و زیان انباشته نزدیک به 67 هزار میلیارد تومان است.
137
- خروجی: company-01 با مدیرعاملی person-01 زیان خالص amount-01 در سه‌ماهه نخست date-01 گزارش کرد. نسبت کفایت سرمایه به منفی percent-01 رسیده و زیان انباشته نزدیک به amount-02 است.
138
-
139
- **نمونه ۹:**
140
- ورودی: دو بانک ملت و پاسارگاد به ترتیب با شناسایی سود خالص 157 و 155 هزار میلیارد ریالی رقابت تنگاتنگی داشته و در رده‌های اول و دوم جای دارند.
141
- خروجی: دو بانک company-01 و company-02 به ترتیب با شناسایی سود خالص amount-01 و amount-02 رقابت تنگاتنگی داشته و در رده‌های اول و دوم جای دارند.
142
-
143
- **نمونه ۱۰:**
144
- ورودی: مرور صورت‌های مالی بانک‌ها نشان می‌دهد سهم سودهای ارزی به‌راحتی به 40–60٪ رسیده است و این مسئله نشان‌دهنده وضعیت غیرعادی بازار است.
145
- خروجی: مرور صورت‌های مالی بانک‌ها نشان می‌دهد سهم سودهای ارزی به‌راحتی به percent-01 رسیده است و این مسئله نشان‌دهنده وضعیت غیرعادی بازار است.
146
-
147
- **فقط متن ناشناس‌شده را برگردان - هیچ توضیح اضافی نیاز نیست."""
148
-
149
- def get_user_prompt(self, text: str) -> str:
150
- """تشکیل پرامپت کاربر"""
151
- return f"""متن مالی فارسی زیر را تجزیه و تحلیل کنید. تمام موجودیت‌های حساس را شناسایی کنید و یک JSON Array برگردانید.
152
-
153
- متن:
154
- {text}
155
-
156
- **مهم**:
157
- - اگر چند بار یک نام تکرار شود، یک id بدهید
158
- - کلمات عمومی را حفظ کنید
159
- - واحدها را حفظ کنید
160
- - فقط JSON برگردانید!
161
-
162
- یک JSON Array برگردانید. هر عنصر دارای:
163
- - "text": متن دقیق استخراج شده
164
- - "type": نوع (company, person, amount, percent, phone, email, date, location, id_number)
165
- - "original": توضیح اضافی اگر نام مستعار باشد"""
166
-
167
- def call_cerebras(self, text: str) -> List[Dict]:
168
- """فراخوانی Cerebras API با پرامپت بهبود شده"""
169
- logger.info("🔄 فراخوانی Cerebras API با دستورالعمل قوی...")
170
-
171
- system_prompt = self.get_system_prompt()
172
- user_prompt = self.get_user_prompt(text)
173
-
174
- try:
175
- response = requests.post(
176
- "https://api.cerebras.ai/v1/chat/completions",
177
- headers={
178
- "Authorization": f"Bearer {self.api_key}",
179
- "Content-Type": "application/json"
180
- },
181
- json={
182
- "model": "llama-3.3-70b",
183
- "messages": [
184
- {"role": "system", "content": system_prompt},
185
- {"role": "user", "content": user_prompt}
186
- ],
187
- "max_tokens": 4000,
188
- "temperature": 0.1
189
- },
190
- timeout=30
191
- )
192
-
193
- if response.status_code != 200:
194
- logger.error(f"❌ خطای API Cerebras: {response.text}")
195
- return []
196
-
197
- result = response.json()
198
- content = result['choices'][0]['message']['content']
199
-
200
- try:
201
- # تمیز کردن محتوا از markdown اگر وجود داشته باشد
202
- content = content.replace("```json", "").replace("```", "").strip()
203
- entities = json.loads(content)
204
- if not isinstance(entities, list):
205
- entities = []
206
- logger.info(f"✅ {len(entities)} موجودیت استخراج شد")
207
- return entities
208
- except json.JSONDecodeError:
209
- logger.error(f"❌ خطا در JSON parsing: {content[:200]}")
210
- return []
211
-
212
- except Exception as e:
213
- logger.error(f"❌ خطا Cerebras: {e}")
214
- return []
215
-
216
- def get_placeholder(self, entity_type: str) -> str:
217
- """تولید placeholder با format جدید"""
218
- type_lower = entity_type.lower()
219
- if type_lower not in self.counters:
220
- type_lower = 'amount'
221
-
222
- self.counters[type_lower] += 1
223
- return f"{type_lower}-{self.counters[type_lower]:02d}"
224
-
225
- def anonymize(self, text: str) -> Tuple[str, List]:
226
- """ناشناس‌سازی متن با قوانین ثبات"""
227
- logger.info("🚀 شروع ناشناس‌سازی متن...")
228
-
229
- # تنظیف
230
- self.mapping_table = {}
231
- self.seen_entities = {}
232
- for key in self.counters:
233
- self.counters[key] = 0
234
-
235
- # دریافت موجودیت‌ها
236
- entities = self.call_cerebras(text)
237
-
238
- if not entities:
239
- logger.warning("⚠️ موجودیتی شناسایی نشد")
240
- return text, []
241
-
242
- logger.info("🔄 Processing entities...")
243
-
244
- # جایگزینی با قانون ثبات
245
- anonymized = text
246
- replacements = []
247
-
248
- for entity in entities:
249
- entity_type = entity.get('type', 'amount').lower()
250
- entity_text = entity.get('text', '').strip()
251
- original_info = entity.get('original', '')
252
-
253
- if not entity_text:
254
- continue
255
-
256
- # بررسی اگر این موجودیت قبلاً دیده شده است
257
- entity_key = (entity_type, entity_text.lower())
258
-
259
- if entity_key in self.seen_entities:
260
- token = self.seen_entities[entity_key]
261
- logger.info(f"🔄 موجودیت تکراری: {entity_text} → {token}")
262
- else:
263
- token = self.get_placeholder(entity_type)
264
- self.seen_entities[entity_key] = token
265
- self.mapping_table[token] = {
266
- 'original': entity_text,
267
- 'type': entity_type,
268
- 'note': original_info
269
- }
270
- logger.info(f"✅ جایگزینی: {entity_text} → {token}")
271
-
272
- # جایگزینی دقیق (case-sensitive اول، سپس case-insensitive)
273
- idx = anonymized.find(entity_text)
274
- if idx != -1:
275
- anonymized = anonymized[:idx] + token + anonymized[idx + len(entity_text):]
276
- replacements.append({
277
- 'original': entity_text,
278
- 'placeholder': token,
279
- 'type': entity_type
280
- })
281
-
282
- logger.info(f"✅ ناشناس‌سازی کامل - {len(self.mapping_table)} نگاشت")
283
- return anonymized, entities
284
-
285
- def get_mapping_table_str(self) -> str:
286
- """جدول نگاشت جزئی"""
287
- if not self.mapping_table:
288
- return "❌ موجودیتی شناسایی نشد"
289
-
290
- result = "## 📊 جدول نگاشت\n\n"
291
- result += "| توکن | اطلاعات اصلی | نوع |\n"
292
- result += "|------|--------|------|\n"
293
-
294
- for token, info in sorted(self.mapping_table.items()):
295
- entity_type = info.get('type', 'unknown')
296
- original = info.get('original', '')
297
- note = info.get('note', '')
298
-
299
- note_str = f" ({note})" if note else ""
300
- result += f"| `{token}` | {original}{note_str} | {entity_type} |\n"
301
-
302
- return result
303
-
304
- def restore(self, text: str) -> str:
305
- """بازگردانی اطلاعات اصلی"""
306
- logger.info("🔄 بازگردانی اطلاعات...")
307
- restored = text
308
- for token, info in self.mapping_table.items():
309
- original = info.get('original', '')
310
- restored = restored.replace(token, original)
311
- logger.info("✅ بازگردانی کامل")
312
- return restored
313
-
314
-
315
- # متغیرهای global
316
- anonymizer = None
317
-
318
- def process(input_text: str, user_prompt: str) -> Tuple[str, str, str, str, str, str]:
319
- """
320
- روند کامل:
321
- 0. اعمال پرامپت بر متن خام (ChatGPT)
322
- 1. ناشناس‌سازی با Cerebras (llama-3.3-70b) + پرامپت قوی
323
- 2. ارسال به ChatGPT (حتما!)
324
- 3. بازگردانی پاسخ ChatGPT
325
- """
326
- global anonymizer
327
-
328
- try:
329
- if not input_text.strip():
330
- return "", "", "", "", "", ""
331
-
332
- # دریافت API Keys
333
- api_key_cerebras = os.getenv("CEREBRAS_API_KEY")
334
- api_key_gpt = os.getenv("OPENAI_API_KEY")
335
-
336
- if not api_key_gpt:
337
- logger.error("❌ OPENAI_API_KEY یافت نشد")
338
- return "", "", "", "", "", ""
339
-
340
- if not api_key_cerebras:
341
- logger.error("❌ CEREBRAS_API_KEY یافت نشد")
342
- return "", "", "", "", "", ""
343
-
344
- # ============================================
345
- # مرحله 0: اعمال پرامپت بر متن خام
346
- # ============================================
347
- logger.info("Step 0: اعمال پرامپت بر متن خام...")
348
-
349
- user_prompt_applied = user_prompt.strip() if user_prompt.strip() else None
350
-
351
- processed_by_gpt = input_text # پیش‌فرض
352
-
353
- # اگر پرامپت خالی است، متن بدون تغییر ماند
354
- if not user_prompt_applied:
355
- logger.info("⏭️ پرامپت خالی است - متن مستقیم به ناشناس‌سازی می‌رود")
356
- else:
357
- logger.info(f"📤 ارسال متن خام به ChatGPT برای اعمال پرامپت کاربر...")
358
- logger.info(f"🎯 پرامپت: {user_prompt_applied}")
359
-
360
- # فقط پرامپت کاربر - بدون دستورالعمل اضافی
361
- prompt_raw_text = f"""{user_prompt_applied}
362
-
363
- متن:
364
- {input_text}"""
365
-
366
- try:
367
- gpt_response_raw = requests.post(
368
- "https://api.openai.com/v1/chat/completions",
369
- headers={"Authorization": f"Bearer {api_key_gpt}"},
370
- json={
371
- "model": "gpt-4o-mini",
372
- "messages": [
373
- {
374
- "role": "system",
375
- "content": "شما دستیار فارسی هستید. درخواست کاربر را دقیقاً انجام بده. فقط آنچه خواسته است کن، بدون افزودن توضیح یا نکات اضافی."
376
- },
377
- {"role": "user", "content": prompt_raw_text}
378
- ],
379
- "max_tokens": 2000,
380
- "temperature": 0.7
381
- },
382
- timeout=60
383
- )
384
-
385
- if gpt_response_raw.status_code == 200:
386
- processed_by_gpt = gpt_response_raw.json()['choices'][0]['message']['content']
387
- logger.info("✅ پرامپت اعمال شد")
388
- else:
389
- error_text = gpt_response_raw.json().get('error', {}).get('message', gpt_response_raw.text)
390
- logger.error(f"❌ خطای ChatGPT (مرحله 0): {error_text}")
391
- processed_by_gpt = input_text # بازگشت به متن اصلی
392
-
393
- except Exception as e:
394
- logger.error(f"❌ خطا در مرحله 0: {e}")
395
- processed_by_gpt = input_text
396
-
397
- # ============================================
398
- # مرحله 1: مقداردهی
399
- # ============================================
400
- if not anonymizer:
401
- logger.info("Initializing anonymizer...")
402
- anonymizer = AnonymizerCerebrasEnhanced()
403
-
404
- # ============================================
405
- # مرحله 2: ناشناس‌سازی متن پردازش‌شده
406
- # ============================================
407
- logger.info("Step 1: ناشناس‌سازی متن پردازش‌شده...")
408
-
409
- anonymized_text, entities = anonymizer.anonymize(processed_by_gpt)
410
-
411
- if not entities:
412
- logger.warning("⚠️ موجودیتی شناسایی نشد - متن ناشناس نشد")
413
- return input_text, processed_by_gpt, "", "", "", ""
414
-
415
- # ============================================
416
- # مرحله 3: جدول نگاشت
417
- # ============================================
418
- logger.info("Step 2: Creating mapping table")
419
- mapping = anonymizer.get_mapping_table_str()
420
- logger.info(f"📋 {len(anonymizer.mapping_table)} نگاشت ایجاد شد")
421
-
422
- # ============================================
423
- # مرحله 4: ارسال متن ناشناس‌شده به ChatGPT
424
- # ============================================
425
- logger.info("Step 3: ارسال متن ناشناس‌شده به ChatGPT...")
426
-
427
- prompt = f"""متن ناشناس‌شده زیر (متن مالی/خبری) را تجزیه و تحلیل کنید.
428
-
429
- متن ناشناس‌شده:
430
- {anonymized_text}
431
-
432
- لطفاً:
433
- 1. متن ناشناس‌شده را تحلیل کنید
434
- 2. نکات اصلی را مشخص کنید
435
- 3. تمام توکن‌های ناشناس (مثل company-01، amount-02، person-01) را حفظ کنید
436
- 4. تنها اطلاعات موجود در متن را بیان کنید"""
437
-
438
- logger.info(f"📤 ارسال به ChatGPT برای تحلیل نهایی...")
439
-
440
- try:
441
- gpt_response_obj = requests.post(
442
- "https://api.openai.com/v1/chat/completions",
443
- headers={"Authorization": f"Bearer {api_key_gpt}"},
444
- json={
445
- "model": "gpt-4o-mini",
446
- "messages": [
447
- {
448
- "role": "system",
449
- "content": "شما دستیار تحلیل متون مالی فارسی هستید. متن‌های ناشناس‌شده را دقیق تحلیل کنید. تمام توکن‌های ناشناس را حفظ کنید."
450
- },
451
- {"role": "user", "content": prompt}
452
- ],
453
- "max_tokens": 1500,
454
- "temperature": 0.7
455
- },
456
- timeout=30
457
- )
458
-
459
- if gpt_response_obj.status_code == 200:
460
- gpt_response = gpt_response_obj.json()['choices'][0]['message']['content']
461
- logger.info("✅ پاسخ دریافت شد")
462
- else:
463
- error_text = gpt_response_obj.json().get('error', {}).get('message', gpt_response_obj.text)
464
- logger.error(f"❌ خطای ChatGPT: {error_text}")
465
- return input_text, processed_by_gpt, anonymized_text, "", "", mapping
466
-
467
- except Exception as e:
468
- logger.error(f"❌ خطا در ارسال به ChatGPT: {e}")
469
- return input_text, processed_by_gpt, anonymized_text, "", "", mapping
470
-
471
- # ============================================
472
- # مرحله 5: بازگردانی پاسخ ChatGPT
473
- # ============================================
474
- logger.info("Step 4: بازگردانی متن اصلی...")
475
-
476
- restored_text = anonymizer.restore(gpt_response)
477
-
478
- logger.info(f"✅ بازگردانی کامل")
479
-
480
- logger.info(f"Done. Input: {len(input_text)} | Processed: {len(processed_by_gpt)} | Anonymized: {len(anonymized_text)} | Entities: {len(entities)}")
481
-
482
- return input_text, processed_by_gpt, anonymized_text, gpt_response, restored_text, mapping
483
-
484
- except Exception as e:
485
- logger.error(f"❌ خطا عمومی: {e}", exc_info=True)
486
- return "", "", "", "", "", ""
487
-
488
- def clear():
489
- """پاک کردن"""
490
- return "", "", "", "", "", ""
491
-
492
- # رابط Gradio
493
- css = """
494
- @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;700&display=swap');
495
-
496
- * {
497
- font-family: 'Vazirmatn', 'Tahoma', 'IranSans', 'B Nazanin', sans-serif !important;
498
- }
499
-
500
- html, body {
501
- direction: rtl !important;
502
- text-align: right !important;
503
- }
504
-
505
- .gradio-container {
506
- direction: rtl !important;
507
- max-width: 1200px !important;
508
- margin: auto !important;
509
- }
510
-
511
- /* Textbox Styling */
512
- .gradio-textbox, .gradio-textbox textarea {
513
- font-family: 'Vazirmatn', 'Tahoma', monospace !important;
514
- font-size: 14px !important;
515
- direction: rtl !important;
516
- text-align: right !important;
517
- line-height: 1.6 !important;
518
- }
519
-
520
- textarea {
521
- direction: rtl !important;
522
- text-align: right !important;
523
- font-family: 'Vazirmatn', 'Tahoma', monospace !important;
524
- }
525
-
526
- input[type="text"], input[type="search"] {
527
- direction: rtl !important;
528
- text-align: right !important;
529
- font-family: 'Vazirmatn', 'Tahoma', sans-serif !important;
530
- }
531
-
532
- /* Label Styling */
533
- label {
534
- font-family: 'Vazirmatn', 'Tahoma', sans-serif !important;
535
- direction: rtl !important;
536
- text-align: right !important;
537
- font-weight: 600 !important;
538
- color: #333 !important;
539
- }
540
-
541
- /* Button Styling */
542
- .gradio-button, button, .gr-button {
543
- font-family: 'Vazirmatn', 'Tahoma', sans-serif !important;
544
- direction: rtl !important;
545
- text-align: center !important;
546
- font-size: 14px !important;
547
- font-weight: 600 !important;
548
- border-radius: 8px !important;
549
- }
550
-
551
- /* Form Elements */
552
- .gr-form, .gr-box, .gr-panel {
553
- direction: rtl !important;
554
- text-align: right !important;
555
- }
556
-
557
- /* Headings */
558
- h1, h2, h3, h4, h5, h6 {
559
- direction: rtl !important;
560
- text-align: right !important;
561
- font-family: 'Vazirmatn', 'Tahoma', sans-serif !important;
562
- font-weight: 700 !important;
563
- }
564
-
565
- /* Markdown Content */
566
- .prose, .markdown {
567
- direction: rtl !important;
568
- text-align: right !important;
569
- }
570
-
571
- /* Row & Column */
572
- .gr-row {
573
- direction: rtl !important;
574
- }
575
-
576
- .gr-column {
577
- direction: rtl !important;
578
- }
579
-
580
- /* Placeholder text */
581
- ::placeholder {
582
- direction: rtl !important;
583
- text-align: right !important;
584
- }
585
-
586
- /* Scrollbar styling */
587
- ::-webkit-scrollbar {
588
- width: 8px !important;
589
- height: 8px !important;
590
- }
591
-
592
- ::-webkit-scrollbar-track {
593
- background: #f1f1f1 !important;
594
- }
595
-
596
- ::-webkit-scrollbar-thumb {
597
- background: #888 !important;
598
- border-radius: 4px !important;
599
- }
600
-
601
- ::-webkit-scrollbar-thumb:hover {
602
- background: #555 !important;
603
- }
604
- """
605
-
606
- with gr.Blocks(title="ناشناس‌سازی متن فارسی", theme=gr.themes.Soft(), css=css) as app:
607
- gr.Markdown("# 🔐 سیستم ناشناس‌سازی متون مالی/خبری فارسی")
608
-
609
- with gr.Row():
610
- with gr.Column(scale=2):
611
- input_text = gr.Textbox(
612
- lines=12,
613
- placeholder="متن را وارد کنید...",
614
- label="📥 متن ورودی"
615
- )
616
-
617
- with gr.Column(scale=1):
618
- user_prompt = gr.Textbox(
619
- lines=12,
620
- placeholder="دستورالعمل خود را بنویسید:\nمثال: خلاصه کن، تحلیل کن، نکات اصلی را بیان کن",
621
- label="📝 پرامپت ورودی",
622
- value="متن مالی/خبری را تحلیل کنید و:\n1. خلاصه مختصر بدهید\n2. نکات اصلی را مشخص کنید\n3. نتایج و تاثیرات را بیان کنید"
623
- )
624
-
625
- with gr.Column(scale=1):
626
- process_btn = gr.Button("⚙️ پردازش", variant="primary", size="lg")
627
- clear_btn = gr.Button("🗑️ پاک کردن", variant="stop")
628
-
629
- with gr.Row():
630
- with gr.Column():
631
- processed_text = gr.Textbox(
632
- lines=8,
633
- label="🤖 متن پردازش‌شده (پس از اعمال پرامپت)",
634
- interactive=False
635
- )
636
-
637
- with gr.Row():
638
- with gr.Column():
639
- anonymized_text = gr.Textbox(
640
- lines=8,
641
- label="🔐 متن ناشناس‌شده",
642
- interactive=False
643
- )
644
-
645
- with gr.Column():
646
- gpt_response = gr.Textbox(
647
- lines=8,
648
- label="💬 تحلیل نهایی (ChatGPT)",
649
- interactive=False
650
- )
651
-
652
- with gr.Row():
653
- with gr.Column():
654
- restored_text = gr.Textbox(
655
- lines=8,
656
- label="✅ متن بازگردانی‌شده",
657
- interactive=False
658
- )
659
-
660
- with gr.Column():
661
- mapping = gr.Textbox(
662
- lines=8,
663
- label="📊 جدول نگاشت",
664
- interactive=False
665
- )
666
-
667
- # Event handlers
668
- process_btn.click(
669
- fn=process,
670
- inputs=[input_text, user_prompt],
671
- outputs=[input_text, processed_text, anonymized_text, gpt_response, restored_text, mapping]
672
- )
673
-
674
- clear_btn.click(
675
- fn=clear,
676
- outputs=[input_text, user_prompt, processed_text, anonymized_text, gpt_response, restored_text, mapping]
677
- )
678
-
679
- if __name__ == "__main__":
680
- print("🚀 سیستم ناشناس‌سازی متن در حال شروع است...")
681
- app.launch(
682
- server_name="0.0.0.0",
683
- server_port=7860,
684
- share=False,
685
- show_error=True,
686
- show_api=False
687
- )