leilaghomashchi commited on
Commit
82299ea
·
verified ·
1 Parent(s): 9e349ec

Upload cerebras_anonymizer.py

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