leilaghomashchi commited on
Commit
dbe9d2f
·
verified ·
1 Parent(s): 6698257

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -740
app.py DELETED
@@ -1,740 +0,0 @@
1
- import requests
2
- import json
3
- import gradio as gr
4
- from typing import Dict, Any, List, Optional
5
- import os
6
- from dataclasses import dataclass
7
- import re
8
- import pandas as pd
9
- import time
10
- from io import StringIO
11
-
12
- @dataclass
13
- class CerebrasConfig:
14
- """تنظیمات Cerebras API"""
15
- api_key: str
16
- base_url: str = "https://api.cerebras.ai/v1"
17
- model: str = "llama-3.3-70b"
18
- max_tokens: int = 2000
19
- temperature: float = 0.1
20
-
21
- class AdvancedCerebrasAnonymizer:
22
- """سیستم پیشرفته ناشناس‌سازی متون مالی/خبری فارسی"""
23
-
24
- def __init__(self, api_key: str = None):
25
- if api_key is None:
26
- api_key = os.getenv("CEREBRAS_API_KEY")
27
- if not api_key:
28
- raise ValueError("کلید API یافت نشد")
29
-
30
- self.config = CerebrasConfig(api_key=api_key)
31
- self.system_prompt = self._create_advanced_system_prompt()
32
-
33
- def _create_advanced_system_prompt(self) -> str:
34
- """ایجاد دستورالعمل سیستمی پیشرفته برای Cerebras"""
35
- return """شما یک «ناشناس‌ساز متون مالی/خبری فارسی» هستید. وظیفه‌تان جایگزینی اسامی خاص و مقادیر عددی با شناسه‌های بی‌معناست.
36
-
37
- ## **قوانین اندیس‌گذاری - CRITICAL**
38
- ### **1. ترتیب شماره‌گذاری الزامی:**
39
- - شرکت‌ها: company-01, company-02, company-03, company-04, ... (پیوسته و بدون گپ)
40
- - اشخاص: person-01, person-02, person-03, ... (پیوسته و بدون گپ)
41
- - اعداد: amount-01, amount-02, amount-03, ... (پیوسته و بدون گپ)
42
- - درصدها: percent-01, percent-02, percent-03, ... (پیوسته و بدون گپ)
43
-
44
- ### **2. ثبات شناسه‌ها در متن:**
45
- - اگر "همراه اول" اول‌بار company-01 شد، در تمام متن همان باشد
46
- - اگر "مهدی احمدی" اول‌بار person-01 شد، در تمام متن همان باشد
47
-
48
- ### **3. تشخیص صحیح انواع:**
49
- **شرکت/سازمان:** همراه اول، بانک ملی، ایران‌خودرو، سایپا، بانک مرکزی، سامانه کدال، وزارت نفت، سازمان تنظیم مقررات رادیویی، سازمان تامین اجتماعی
50
- **⚠️ CRITICAL - گروه‌ها:** "گروه همراه اول"، "گروه اقتصادی آزادگان"، "گروه مالی صبا" → همه company-XX هستند (نه group-XX)
51
- **⚠️ CRITICAL - کلمات عمومی:** "سه شرکت دارویی"، "چند بانک"، "یک شرکت" → کلمات عمومی هستند، موجودیت نیستند (حفظ شوند)
52
- **⚠️ CRITICAL - نام‌های مستعار:** "فاما" همان "فولاد مبارکه اصفهان" است → هر دو company-01
53
- **شخص:** مهدی اخوان بهابادی، محمدرضا فرزین، ابوالفضل نجارزاده
54
- **عدد:** 37، 70، 677، 73.7، 178 (هر عددی)
55
- **درصد:** 37 درصدی، 15 درصدی، 53 درصد، 43%
56
-
57
- ## **مثال‌های صحیح:**
58
-
59
- ### **مثال 1 (الگوی کامل):**
60
- **ورودی:** مهدی اخوان بهابادی، مدیرعامل همراه اول، اعلام کرد درآمد عملیاتی شرکت با رشد 37 درصدی به 70 هزار و 677 میلیارد تومان رسیده است. سود خالص 7101 میلیارد تومان و تلفیقی گروه همراه اول 8003 میلیارد تومان شد.
61
- **خروجی صحیح:** person-01، مدیرعامل company-01، اعلام کرد درآمد عملیاتی شرکت با رشد percent-01 به amount-01 رسیده است. سود خالص amount-02 و تلفیقی گروه company-01 amount-03 شد.
62
-
63
- ### **مثال 2:**
64
- **ورودی:** بانک مرکزی و بانک ملی با همکاری محمدرضا فرزین، 60 درصد سپرده‌ها را مدیریت کردند.
65
- **خروجی:** company-01 و company-02 با همکاری person-01، percent-01 سپرده‌ها را مدیریت کردند.
66
-
67
- ## **موارد حفظ شده:**
68
- - تاریخ‌ها: 1404/04/23، 30 آذر 1403، پاییز 1401
69
- - فصل‌های سال: پاییز، بهار، تابستان، زمستان
70
- - عناوین شغلی: مدیرعامل، رئیس کل، مدیرکل
71
- - واحدها: میلیارد تومان، همت، ریال، ماه، سال
72
- - مکان‌ها: تهران، اصفهان، ایران
73
- - کلمات عمومی: "سه شرکت دارویی"، "چند بانک"، "یک شرکت"، "مراکز درمانی"
74
- - دوره‌های زمانی: "۵ ماهه سال"، "۹ ماهه"، "۳ ماهه اول"
75
-
76
- ## **ممنوع:**
77
- - کلمات انگلیسی اضافی
78
- - تغییر ساختار جمله
79
- - حذف یا اضافه کردن کلمات
80
- - استفاده از group-XX - همه گروه‌ها باید company-XX باشند
81
-
82
- **فقط متن ناشناس‌شده را برگردان - هیچ توضیح اضافی نیاز نیست.**
83
- """
84
-
85
- def _make_api_request(self, text: str, max_retries: int = 5) -> Dict[str, Any]:
86
- """ارسال درخواست به Cerebras API با retry logic"""
87
- headers = {
88
- "Authorization": f"Bearer {self.config.api_key}",
89
- "Content-Type": "application/json"
90
- }
91
-
92
- payload = {
93
- "messages": [
94
- {"role": "system", "content": self.system_prompt},
95
- {"role": "user", "content": text}
96
- ],
97
- "model": self.config.model,
98
- "temperature": self.config.temperature,
99
- "max_tokens": self.config.max_tokens
100
- }
101
-
102
- for attempt in range(max_retries):
103
- try:
104
- response = requests.post(
105
- f"{self.config.base_url}/chat/completions",
106
- headers=headers,
107
- json=payload,
108
- timeout=60
109
- )
110
- response.raise_for_status()
111
- return response.json()
112
-
113
- except requests.exceptions.RequestException as e:
114
- if "429" in str(e):
115
- # Rate limit - exponential backoff
116
- wait_time = (2 ** attempt) * 10 # 10, 20, 40, 80, 160 ثانیه
117
- print(f"⚠️ Rate limit! تلاش {attempt + 1}/{max_retries}. صبر {wait_time} ثانیه...")
118
- time.sleep(wait_time)
119
- if attempt == max_retries - 1:
120
- raise Exception(f"خطا در ارتباط با Cerebras API پس از {max_retries} تلاش: {str(e)}")
121
- else:
122
- raise Exception(f"خطا در ارتباط با Cerebras API: {str(e)}")
123
-
124
- def _clean_markdown(self, content: str) -> str:
125
- """پاک کردن markdown از پاسخ"""
126
- if "```" in content:
127
- lines = content.split('\n')
128
- clean_lines = []
129
- skip = False
130
- for line in lines:
131
- if line.strip().startswith('```'):
132
- skip = not skip
133
- continue
134
- if not skip:
135
- clean_lines.append(line)
136
- content = '\n'.join(clean_lines)
137
- return content
138
-
139
- def _analyze_anonymized_text(self, text: str) -> Dict[str, Any]:
140
- """تحلیل متن ناشناس‌سازی شده"""
141
- companies = re.findall(r'company-(\d+)', text)
142
- persons = re.findall(r'person-(\d+)', text)
143
- amounts = re.findall(r'amount-(\d+)', text)
144
- percents = re.findall(r'percent-(\d+)', text)
145
-
146
- statistics = {
147
- "company": len(set(companies)),
148
- "person": len(set(persons)),
149
- "amount": len(set(amounts)),
150
- "percent": len(set(percents)),
151
- "total_replacements": len(companies) + len(persons) + len(amounts) + len(percents)
152
- }
153
-
154
- entities = {
155
- "companies": sorted(list(set(companies)), key=lambda x: int(x)),
156
- "persons": sorted(list(set(persons)), key=lambda x: int(x)),
157
- "amounts": sorted(list(set(amounts)), key=lambda x: int(x)),
158
- "percents": sorted(list(set(percents)), key=lambda x: int(x))
159
- }
160
-
161
- detailed_analysis = {
162
- "preserved_dates": len(re.findall(r'\d{4}/\d{1,2}/\d{1,2}|\d{1,2}\s+\w+\s+\d{4}', text)),
163
- "preserved_times": len(re.findall(r'\d{1,2}:\d{2}', text)),
164
- "financial_indicators": len(re.findall(r'\b(EPS|P/E|ARPU|NPL|ROE|ROA)\b', text)),
165
- "units_preserved": len(re.findall(r'(میلیارد|میلیون|هزار|تومان|ریال|درهم|دلار|یورو|تن|کیلوگرم)', text))
166
- }
167
-
168
- return {
169
- "statistics": statistics,
170
- "entities": entities,
171
- "detailed_analysis": detailed_analysis
172
- }
173
-
174
- def _validate_anonymized_text(self, text: str) -> Dict[str, Any]:
175
- """اعتبارسنجی پیشرفته متن ناشناس‌شده"""
176
- companies = re.findall(r'company-(\d+)', text)
177
- persons = re.findall(r'person-(\d+)', text)
178
- amounts = re.findall(r'amount-(\d+)', text)
179
- percents = re.findall(r'percent-(\d+)', text)
180
-
181
- validation_issues = []
182
-
183
- for entity_type, indices in [
184
- ("company", companies),
185
- ("person", persons),
186
- ("amount", amounts),
187
- ("percent", percents)
188
- ]:
189
- if indices:
190
- unique_indices = sorted(list(set([int(x) for x in indices])))
191
- if unique_indices[0] != 1:
192
- validation_issues.append(f"اندیس {entity_type} از 01 شروع نشده")
193
- expected = list(range(1, len(unique_indices) + 1))
194
- if unique_indices != expected:
195
- validation_issues.append(f"اندیس‌های {entity_type} پیوسته نیستند")
196
-
197
- return {
198
- "is_valid": len(validation_issues) == 0,
199
- "issues": validation_issues,
200
- "entity_counts": {
201
- "company": len(set(companies)),
202
- "person": len(set(persons)),
203
- "amount": len(set(amounts)),
204
- "percent": len(set(percents))
205
- }
206
- }
207
-
208
- def anonymize_text(self, text: str) -> Dict[str, Any]:
209
- """ناشناس‌سازی متن با استفاده از Cerebras"""
210
- if not text or not text.strip():
211
- return {
212
- "success": False,
213
- "error": "متن ورودی خالی است"
214
- }
215
-
216
- try:
217
- response = self._make_api_request(text)
218
-
219
- if "choices" not in response or not response["choices"]:
220
- return {
221
- "success": False,
222
- "error": "پاسخ نامعتبر از API"
223
- }
224
-
225
- content = response["choices"][0]["message"]["content"]
226
- content = self._clean_markdown(content)
227
- content = content.strip()
228
-
229
- analysis = self._analyze_anonymized_text(content)
230
-
231
- return {
232
- "success": True,
233
- "anonymized_text": content,
234
- "entities": analysis["entities"],
235
- "statistics": analysis["statistics"],
236
- "detailed_analysis": analysis["detailed_analysis"],
237
- "usage": response.get("usage", {}),
238
- "quality_check": self._validate_anonymized_text(content)
239
- }
240
-
241
- except Exception as e:
242
- return {
243
- "success": False,
244
- "error": f"خطا در پردازش: {str(e)}"
245
- }
246
-
247
- def anonymize_batch(self, texts: List[str], progress_callback=None) -> List[Dict[str, Any]]:
248
- """ناشناس‌سازی دسته‌ای متون"""
249
- results = []
250
- total = len(texts)
251
-
252
- for idx, text in enumerate(texts):
253
- if progress_callback:
254
- progress_callback((idx + 1) / total, f"پردازش سطر {idx + 1} از {total}")
255
-
256
- result = self.anonymize_text(text)
257
- results.append(result)
258
-
259
- # تاخیر برای جلوگیری از rate limiting
260
- if idx < total - 1:
261
- time.sleep(3) # افزایش به 3 ثانیه برای جلوگیری از rate limit
262
-
263
- return results
264
-
265
-
266
- def create_advanced_interface():
267
- """ایجاد رابط کاربری پیشرفته با قابلیت پردازش دسته‌ای"""
268
-
269
- api_key_available = bool(os.getenv("CEREBRAS_API_KEY"))
270
-
271
- custom_css = """
272
- .gradio-container {
273
- font-family: 'Tahoma', 'Arial', sans-serif !important;
274
- direction: rtl;
275
- max-width: 1400px;
276
- margin: 0 auto;
277
- }
278
- .result-box {
279
- background-color: #f8f9fa;
280
- border: 2px solid #e9ecef;
281
- border-radius: 12px;
282
- padding: 20px;
283
- margin: 10px 0;
284
- }
285
- .warning-box {
286
- background-color: #fff3cd;
287
- border: 2px solid #ffeaa7;
288
- border-radius: 12px;
289
- padding: 15px;
290
- color: #856404;
291
- margin: 10px 0;
292
- }
293
- .success-box {
294
- background-color: #d4edda;
295
- border: 2px solid #c3e6cb;
296
- border-radius: 12px;
297
- padding: 15px;
298
- color: #155724;
299
- margin: 10px 0;
300
- }
301
- .batch-progress {
302
- background-color: #e3f2fd;
303
- border: 2px solid #90caf9;
304
- border-radius: 12px;
305
- padding: 15px;
306
- margin: 10px 0;
307
- }
308
- """
309
-
310
- with gr.Blocks(css=custom_css, title="ناشناس‌ساز پیشرفته متن فارسی با Cerebras", theme=gr.themes.Soft()) as interface:
311
-
312
- gr.Markdown("""
313
- # 🔒 سیستم پیشرفته ناشناس‌سازی متون مالی/خبری فارسی
314
- ### ⚡ قدرت‌گرفته از Cerebras AI - سریع‌ترین استنباط LLM در جهان!
315
- """)
316
-
317
- # API Key input
318
- if api_key_available:
319
- gr.Markdown("""
320
- <div class="success-box">
321
- ✅ <strong>سیستم آماده است</strong> - کلید API تنظیم شده
322
- </div>
323
- """)
324
- api_key_input = gr.Textbox(visible=False, value="")
325
- else:
326
- gr.Markdown("""
327
- <div class="warning-box">
328
- ⚠️ <strong>کلید API تنظیم نشده</strong><br>
329
- لطفاً کلید Cerebras API خود را در زیر وارد کنید
330
- </div>
331
- """)
332
- api_key_input = gr.Textbox(
333
- label="🔑 کلید Cerebras API",
334
- placeholder="csk-...",
335
- type="password"
336
- )
337
-
338
- # تب‌های اصلی
339
- with gr.Tabs() as tabs:
340
-
341
- # ===============================
342
- # تب 1: پردازش تکی
343
- # ===============================
344
- with gr.TabItem("📝 پردازش تکی"):
345
- with gr.Row():
346
- with gr.Column(scale=1):
347
- input_text = gr.Textbox(
348
- label="📝 متن ورودی",
349
- placeholder="متن مالی یا خبری خود را اینجا وارد کنید...",
350
- lines=12,
351
- max_lines=25
352
- )
353
-
354
- with gr.Row():
355
- anonymize_btn = gr.Button("🔒 ناشناس‌سازی", variant="primary", size="lg")
356
- clear_btn = gr.Button("🗑️ پاک کردن", variant="secondary")
357
-
358
- with gr.Column(scale=1):
359
- output_text = gr.Textbox(
360
- label="🎯 متن ناشناس‌سازی شده",
361
- lines=12,
362
- max_lines=25,
363
- elem_classes=["result-box"]
364
- )
365
- copy_btn = gr.Button("📋 کپی متن", variant="secondary", size="sm")
366
-
367
- with gr.Row():
368
- with gr.Column(scale=1):
369
- statistics_output = gr.Markdown(label="📊 آمار")
370
- with gr.Column(scale=1):
371
- quality_output = gr.Markdown(label="✅ کنترل کیفیت")
372
-
373
- with gr.Accordion("📋 جزئیات موجودیت‌ها", open=False):
374
- entities_output = gr.Markdown()
375
-
376
- with gr.Accordion("📈 تحلیل تفصیلی", open=False):
377
- detailed_analysis_output = gr.Markdown()
378
-
379
- usage_output = gr.Markdown()
380
- copy_output = gr.Textbox(visible=False, label="متن کپی شده")
381
-
382
- # ===============================
383
- # تب 2: پردازش دسته‌ای
384
- # ===============================
385
- with gr.TabItem("📁 پردازش دسته‌ای CSV"):
386
- gr.Markdown("""
387
- ### 📁 پردازش دسته‌ای فایل CSV
388
- فایل CSV خود را آپلود کنید، ستون متن را انتخاب کنید و تمام سطرها به صورت خودکار ناشناس‌سازی می‌شوند.
389
- """)
390
-
391
- with gr.Row():
392
- with gr.Column(scale=1):
393
- csv_file = gr.File(
394
- label="📤 آپلود فایل CSV",
395
- file_types=[".csv"],
396
- type="filepath"
397
- )
398
-
399
- text_column = gr.Dropdown(
400
- label="📋 انتخاب ستون متن",
401
- choices=[],
402
- interactive=True
403
- )
404
-
405
- output_column_name = gr.Textbox(
406
- label="🏷️ نام ستون خروجی",
407
- value="anonymized_text",
408
- placeholder="نام ستون جدید برای متن ناشناس‌شده"
409
- )
410
-
411
- with gr.Row():
412
- batch_btn = gr.Button("🚀 شروع پردازش دسته‌ای", variant="primary", size="lg")
413
- cancel_btn = gr.Button("❌ لغو", variant="stop", size="sm")
414
-
415
- with gr.Column(scale=1):
416
- batch_progress = gr.Markdown(
417
- value="⏳ در انتظار آپلود فایل...",
418
- elem_classes=["batch-progress"]
419
- )
420
-
421
- batch_stats = gr.Markdown(label="📊 آمار پردازش")
422
-
423
- # پیش‌نمایش داده‌ها
424
- with gr.Accordion("👁️ پیش‌نمایش داده‌ها", open=True):
425
- preview_df = gr.Dataframe(
426
- label="پیش‌نمایش 5 سطر اول",
427
- headers=["ستون‌ها"],
428
- interactive=False
429
- )
430
-
431
- # نتایج و دانلود
432
- with gr.Row():
433
- result_df = gr.Dataframe(
434
- label="📊 نتایج پردازش (10 سطر اول)",
435
- interactive=False,
436
- visible=False
437
- )
438
-
439
- download_btn = gr.File(
440
- label="📥 دانلود فایل خروجی",
441
- visible=False
442
- )
443
-
444
- # لاگ خطاها
445
- with gr.Accordion("⚠️ گزارش خطاها", open=False):
446
- error_log = gr.Markdown()
447
-
448
- # ===============================
449
- # راهنما
450
- # ===============================
451
- with gr.Accordion("📖 راهنمای استفاده", open=False):
452
- gr.Markdown("""
453
- ## 🎯 ویژگی‌های سیستم:
454
-
455
- ### 📝 پردازش تکی:
456
- - متن خود را وارد کنید و روی دکمه ناشناس‌سازی کلیک کنید
457
- - نتایج شامل آمار، کنترل کیفیت و جزئیات موجودیت‌ها است
458
-
459
- ### 📁 پردازش دسته‌ای CSV:
460
- 1. فایل CSV خود را آپلود کنید
461
- 2. ستون حاوی متن را انتخاب کنید
462
- 3. نام ستون خروجی را مشخص کنید
463
- 4. روی «شروع پردازش دسته‌ای» کلیک کنید
464
- 5. پس از اتمام، فایل خروجی را دانلود کنید
465
-
466
- ### 🏷️ انواع برچسب‌ها:
467
- - **company-XX:** شرکت‌ها، سازمان‌ها، برندها
468
- - **person-XX:** اشخاص حقیقی
469
- - **amount-XX:** اعداد و مبالغ
470
- - **percent-XX:** درصدها
471
-
472
- ### ⚡ نکات مهم:
473
- - حداکثر 1000 سطر در هر فایل پشتیبانی می‌شود
474
- - فایل CSV باید با encoding UTF-8 ذخیره شده باشد
475
- - پردازش هر سطر حدود 2-3 ثانیه زمان می‌برد
476
- """)
477
-
478
- # ===============================
479
- # توابع کمکی
480
- # ===============================
481
-
482
- def load_csv_columns(file_path):
483
- """بارگذاری ستون‌های فایل CSV"""
484
- if file_path is None:
485
- return gr.Dropdown(choices=[]), None, "⏳ در انتظار آپلود فایل..."
486
-
487
- try:
488
- df = pd.read_csv(file_path, encoding='utf-8')
489
- except:
490
- try:
491
- df = pd.read_csv(file_path, encoding='utf-8-sig')
492
- except:
493
- df = pd.read_csv(file_path, encoding='cp1256')
494
-
495
- columns = df.columns.tolist()
496
- preview = df.head(5)
497
-
498
- status = f"✅ فایل بارگذاری شد | **{len(df)}** سطر | **{len(columns)}** ستون"
499
-
500
- return gr.Dropdown(choices=columns, value=columns[0] if columns else None), preview, status
501
-
502
- def process_single_text(text, api_key):
503
- """پردازش تکی متن"""
504
- if not text or not text.strip():
505
- return "", "⚠️ لطفاً متن وارد کنید", "", "", "", ""
506
-
507
- try:
508
- key = api_key if api_key else os.getenv("CEREBRAS_API_KEY")
509
- anonymizer = AdvancedCerebrasAnonymizer(api_key=key)
510
- result = anonymizer.anonymize_text(text)
511
-
512
- if not result["success"]:
513
- return "", f"❌ خطا: {result['error']}", "", "", "", ""
514
-
515
- # آمار
516
- stats = result["statistics"]
517
- stats_md = f"""
518
- ### 📊 آمار موجودیت‌ها:
519
- | نوع | تعداد |
520
- |-----|-------|
521
- | 🏢 شرکت/سازمان | {stats['company']} |
522
- | 👤 شخص | {stats['person']} |
523
- | 💰 مبلغ/عدد | {stats['amount']} |
524
- | 📈 درصد | {stats['percent']} |
525
- | **کل جایگزینی‌ها** | **{stats['total_replacements']}** |
526
- """
527
-
528
- # کنترل کیفیت
529
- quality = result["quality_check"]
530
- if quality["is_valid"]:
531
- quality_md = "✅ **کیفیت تأیید شد** - بدون مشکل"
532
- else:
533
- issues = "\n".join([f"- {issue}" for issue in quality["issues"]])
534
- quality_md = f"⚠️ **مشکلات شناسایی شده:**\n{issues}"
535
-
536
- # موجودیت‌ها
537
- entities = result["entities"]
538
- entities_md = f"""
539
- **شرکت‌ها:** {', '.join([f'company-{x}' for x in entities['companies']]) or 'ندارد'}
540
- **اشخاص:** {', '.join([f'person-{x}' for x in entities['persons']]) or 'ندارد'}
541
- **مبالغ:** {', '.join([f'amount-{x}' for x in entities['amounts']]) or 'ندارد'}
542
- **درصدها:** {', '.join([f'percent-{x}' for x in entities['percents']]) or 'ندارد'}
543
- """
544
-
545
- # تحلیل تفصیلی
546
- detailed = result["detailed_analysis"]
547
- detailed_md = f"""
548
- | شاخص | مقدار |
549
- |------|-------|
550
- | تاریخ‌های حفظ شده | {detailed['preserved_dates']} |
551
- | شاخص‌های مالی | {detailed['financial_indicators']} |
552
- | واحدهای حفظ شده | {detailed['units_preserved']} |
553
- """
554
-
555
- # مصرف
556
- usage = result.get("usage", {})
557
- usage_md = f"⚡ **توکن‌ها:** ورودی: {usage.get('prompt_tokens', '-')} | خروجی: {usage.get('completion_tokens', '-')}"
558
-
559
- return (
560
- result["anonymized_text"],
561
- stats_md,
562
- quality_md,
563
- entities_md,
564
- detailed_md,
565
- usage_md
566
- )
567
-
568
- except Exception as e:
569
- return "", f"❌ خطا: {str(e)}", "", "", "", ""
570
-
571
- def process_batch_csv(file_path, text_col, output_col, api_key, progress=gr.Progress()):
572
- """پردازش دسته‌ای فایل CSV"""
573
- if file_path is None:
574
- return None, "❌ لطفاً فایل CSV آپلود کنید", "", gr.File(visible=False), None
575
-
576
- if not text_col:
577
- return None, "❌ لطفاً ستون متن را انتخاب کنید", "", gr.File(visible=False), None
578
-
579
- try:
580
- # خواندن فایل
581
- try:
582
- df = pd.read_csv(file_path, encoding='utf-8')
583
- except:
584
- try:
585
- df = pd.read_csv(file_path, encoding='utf-8-sig')
586
- except:
587
- df = pd.read_csv(file_path, encoding='cp1256')
588
-
589
- if text_col not in df.columns:
590
- return None, f"❌ ستون '{text_col}' در فایل یافت نشد", "", gr.File(visible=False), None
591
-
592
- # محدودیت تعداد سطرها
593
- max_rows = 1000
594
- if len(df) > max_rows:
595
- return None, f"❌ تعداد سطرها ({len(df)}) از حداکثر مجاز ({max_rows}) بیشتر است", "", gr.File(visible=False), None
596
-
597
- # ایجاد anonymizer
598
- key = api_key if api_key else os.getenv("CEREBRAS_API_KEY")
599
- if not key:
600
- return None, "❌ کلید API تنظیم نشده", "", gr.File(visible=False), None
601
-
602
- anonymizer = AdvancedCerebrasAnonymizer(api_key=key)
603
-
604
- # پردازش سطرها
605
- total = len(df)
606
- anonymized_texts = []
607
- error_rows = []
608
- success_count = 0
609
-
610
- progress(0, desc="شروع پردازش...")
611
-
612
- for idx, row in df.iterrows():
613
- text = str(row[text_col])
614
-
615
- progress((idx + 1) / total, desc=f"پردازش سطر {idx + 1} از {total}")
616
-
617
- if not text or text.strip() == '' or text.lower() == 'nan':
618
- anonymized_texts.append("")
619
- continue
620
-
621
- result = anonymizer.anonymize_text(text)
622
-
623
- if result["success"]:
624
- anonymized_texts.append(result["anonymized_text"])
625
- success_count += 1
626
- else:
627
- anonymized_texts.append(f"[خطا: {result['error']}]")
628
- error_rows.append(f"سطر {idx + 1}: {result['error']}")
629
-
630
- # تاخیر برای جلوگیری از rate limit
631
- time.sleep(3) # افزایش به 3 ثانیه
632
-
633
- # اضافه کردن ستون جدید
634
- output_col_name = output_col if output_col else "anonymized_text"
635
- df[output_col_name] = anonymized_texts
636
-
637
- # ذخیره فایل خروجی
638
- output_path = "/tmp/anonymized_output.csv"
639
- df.to_csv(output_path, index=False, encoding='utf-8-sig')
640
-
641
- # آمار
642
- stats_md = f"""
643
- ### 📊 آمار پردازش:
644
- | شاخص | مقدار |
645
- |------|-------|
646
- | کل سطرها | {total} |
647
- | پردازش موفق | {success_count} |
648
- | خطا | {len(error_rows)} |
649
- | درصد موفقیت | {(success_count/total*100):.1f}% |
650
- """
651
-
652
- # گزارش خطاها
653
- error_md = ""
654
- if error_rows:
655
- error_md = "### ⚠️ خطاهای مشاهده شده:\n" + "\n".join([f"- {e}" for e in error_rows[:20]])
656
- if len(error_rows) > 20:
657
- error_md += f"\n... و {len(error_rows) - 20} خطای دیگر"
658
-
659
- # نمایش نتایج
660
- result_preview = df[[text_col, output_col_name]].head(10)
661
-
662
- return (
663
- result_preview,
664
- f"✅ **پردازش کامل شد!** | {success_count} سطر با موفقیت",
665
- stats_md,
666
- gr.File(value=output_path, visible=True),
667
- error_md
668
- )
669
-
670
- except Exception as e:
671
- return None, f"❌ خطا در پردازش: {str(e)}", "", gr.File(visible=False), str(e)
672
-
673
- def copy_text(text_to_copy):
674
- """کپی متن"""
675
- if not text_to_copy or not text_to_copy.strip():
676
- return gr.Textbox(visible=False), "⚠️ متنی برای کپی وجود ندارد"
677
- return gr.Textbox(value=text_to_copy, visible=True), "✅ متن کپی شد"
678
-
679
- def clear_all():
680
- """پاک کردن فیلدها"""
681
- return "", "", "", "", "", "", "", gr.Textbox(visible=False)
682
-
683
- # ===============================
684
- # اتصال رویدادها
685
- # ===============================
686
-
687
- # پردازش تکی
688
- anonymize_btn.click(
689
- fn=process_single_text,
690
- inputs=[input_text, api_key_input],
691
- outputs=[output_text, statistics_output, quality_output, entities_output, detailed_analysis_output, usage_output]
692
- )
693
-
694
- copy_btn.click(
695
- fn=copy_text,
696
- inputs=[output_text],
697
- outputs=[copy_output, statistics_output]
698
- )
699
-
700
- clear_btn.click(
701
- fn=clear_all,
702
- outputs=[input_text, output_text, statistics_output, quality_output, entities_output, detailed_analysis_output, usage_output, copy_output]
703
- )
704
-
705
- # پردازش دسته‌ای
706
- csv_file.change(
707
- fn=load_csv_columns,
708
- inputs=[csv_file],
709
- outputs=[text_column, preview_df, batch_progress]
710
- )
711
-
712
- batch_btn.click(
713
- fn=process_batch_csv,
714
- inputs=[csv_file, text_column, output_column_name, api_key_input],
715
- outputs=[result_df, batch_progress, batch_stats, download_btn, error_log]
716
- )
717
-
718
- # مثال‌ها
719
- gr.Examples(
720
- examples=[
721
- ["مهدی اخوان بهابادی، مدیرعامل همراه اول، اعلام کرد درآمد عملیاتی شرکت با رشد 37 درصدی به 70 هزار و 677 میلیارد تومان رسیده است."],
722
- ["بانک مرکزی و بانک ملی با همکاری محمدرضا فرزین، 60 درصد سپرده‌ها را مدیریت کردند."],
723
- ["سازمان تامین اجتماعی دارای سه شرکت دارویی است که از مراکز درمانی وابسته به وزارت بهداشت مطالباتی دارند."]
724
- ],
725
- inputs=input_text,
726
- label="📚 مثال‌ها"
727
- )
728
-
729
- return interface
730
-
731
-
732
- # اجرای برنامه
733
- if __name__ == "__main__":
734
- interface = create_advanced_interface()
735
- interface.launch(
736
- server_name="0.0.0.0",
737
- server_port=7860,
738
- share=True,
739
- show_error=True
740
- )