leilaghomashchi commited on
Commit
0d78fe8
·
verified ·
1 Parent(s): 9112046

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +348 -96
app.py CHANGED
@@ -1,136 +1,388 @@
1
  """
2
- OpenRouter API Debug & Test Script
3
- این اسکریپت مشکلات احتمالی را تشخیص می‌دهد
4
  """
5
 
6
  import requests
7
  import json
 
 
8
  import os
 
 
9
 
10
- def test_openrouter_connection():
11
- """تست اتصال به OpenRouter"""
12
-
13
- api_key = os.getenv("OPENROUTER_API_KEY")
14
-
15
- if not api_key:
16
- print("❌ کلید API یافت نشد!")
17
- print("لطفاً OPENROUTER_API_KEY را در Settings تنظیم کنید")
18
- return
19
-
20
- print(f"✅ API Key موجود است (طول: {len(api_key)})")
21
- print(f"🔑 شروع کلید: {api_key[:10]}...")
 
22
 
23
- # لیست مدل‌های رایگان مختلف برای تست
24
- models_to_test = [
25
- "qwen/qwen-2.5-7b-instruct:free",
26
- "meta-llama/llama-3.2-3b-instruct:free",
27
  "microsoft/phi-3-mini-128k-instruct:free",
28
  "google/gemini-2.0-flash-exp:free",
29
- "qwen/qwen-2.5-14b-instruct:free"
30
  ]
31
 
32
- url = "https://openrouter.ai/api/v1/chat/completions"
33
-
34
- test_message = "سلام"
35
-
36
- for model in models_to_test:
37
- print(f"\n{'='*60}")
38
- print(f"🧪 تست مدل: {model}")
39
- print(f"{'='*60}")
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  headers = {
42
- "Authorization": f"Bearer {api_key}",
43
  "Content-Type": "application/json"
44
  }
45
 
46
  payload = {
47
  "model": model,
48
  "messages": [
49
- {"role": "user", "content": test_message}
 
50
  ],
51
- "max_tokens": 100
 
 
52
  }
53
 
54
  try:
55
- print("📡 در حال ارسال درخواست...")
56
- response = requests.post(url, headers=headers, json=payload, timeout=30)
57
-
58
- print(f"📊 Status Code: {response.status_code}")
 
 
59
 
60
  if response.status_code == 200:
61
- print("✅ موفق!")
62
- result = response.json()
63
- content = result['choices'][0]['message']['content']
64
- print(f"📝 پاسخ: {content[:100]}...")
65
- print(f"💰 Usage: {result.get('usage', {})}")
66
- print(f"\n🎉 این مدل کار می‌کند: {model}")
67
- return model
68
  else:
69
- print(f" خطا!")
70
- print(f"📄 پاسخ سرور:")
71
- try:
72
- error_data = response.json()
73
- print(json.dumps(error_data, indent=2, ensure_ascii=False))
74
- except:
75
- print(response.text)
76
-
77
  except Exception as e:
78
- print(f" Exception: {str(e)}")
 
79
 
80
- print("\n" + "="*60)
81
- print(" هیچ مدلی کار نکرد!")
82
- print("\n💡 راهنمایی:")
83
- print("1. بررسی کنید کلید API معتبر است")
84
- print("2. به https://openrouter.ai/keys بروید و کلید جدید بگیرید")
85
- print("3. مطمئن شوید کردیت کافی دارید")
86
- print("4. لیست مدل‌های موجود: https://openrouter.ai/docs/models")
87
-
88
- def get_available_models():
89
- """لیست مدل‌های موجود"""
90
- api_key = os.getenv("OPENROUTER_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- if not api_key:
93
- print("❌ کلید API یافت نشد!")
94
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
- print("\n📋 در حال دریافت لیست مدل‌ها...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- try:
99
- headers = {
100
- "Authorization": f"Bearer {api_key}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
 
 
 
 
 
 
 
 
 
 
 
102
 
103
- response = requests.get(
104
- "https://openrouter.ai/api/v1/models",
105
- headers=headers,
106
- timeout=30
107
- )
108
 
109
- if response.status_code == 200:
110
- models = response.json()
111
- free_models = [m for m in models.get('data', []) if ':free' in m.get('id', '')]
112
 
113
- print(f"\n✅ تعداد مدل‌های رایگان: {len(free_models)}")
114
- print("\n🆓 مدل‌های رایگان موجود:")
115
- for model in free_models[:20]:
116
- print(f" - {model['id']}")
 
 
 
 
 
117
  else:
118
- print(f" خطا: {response.status_code}")
119
- print(response.text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- except Exception as e:
122
- print(f"❌ خطا: {str(e)}")
123
 
124
  if __name__ == "__main__":
125
- print("🔍 OpenRouter Connection Debugger")
126
- print("="*60)
127
-
128
- # تست اتصال
129
- working_model = test_openrouter_connection()
130
-
131
- # دریافت لیست مدل‌ها
132
- # get_available_models()
133
-
134
- if working_model:
135
- print(f"\n\n✅ مدل کاری: {working_model}")
136
- print("این را در کد اصلی استفاده کنید!")
 
1
  """
2
+ ناشناس‌ساز پیشرفته متون مالی/خبری فارسی
3
+ نسخه: 1.2.0 - با مدل تست شده Llama 3.2
4
  """
5
 
6
  import requests
7
  import json
8
+ import gradio as gr
9
+ from typing import Dict, Any, Optional
10
  import os
11
+ from dataclasses import dataclass
12
+ import re
13
 
14
+ @dataclass
15
+ class OpenRouterConfig:
16
+ """تنظیمات OpenRouter API"""
17
+ api_key: str
18
+ base_url: str = "https://openrouter.ai/api/v1"
19
+ # مدل تست شده و کاری
20
+ model: str = "meta-llama/llama-3.2-3b-instruct:free"
21
+ max_tokens: int = 8192
22
+ temperature: float = 0.6
23
+ top_p: float = 0.85
24
+
25
+ class AdvancedOpenRouterAnonymizer:
26
+ """سیستم پیشرفته ناشناس‌سازی"""
27
 
28
+ # لیست مدل‌های رایگان با اولویت (از نتیجه تست)
29
+ AVAILABLE_MODELS = [
30
+ "meta-llama/llama-3.2-3b-instruct:free", # ✅ تست شده
 
31
  "microsoft/phi-3-mini-128k-instruct:free",
32
  "google/gemini-2.0-flash-exp:free",
 
33
  ]
34
 
35
+ def __init__(self, api_key: str = None):
36
+ if api_key is None:
37
+ api_key = os.getenv("OPENROUTER_API_KEY")
38
+ if not api_key:
39
+ raise ValueError("کلید API یافت نشد")
 
 
 
40
 
41
+ self.config = OpenRouterConfig(api_key=api_key)
42
+ self.working_model = None
43
+ self.system_prompt = self._create_system_prompt()
44
+
45
+ def _create_system_prompt(self) -> str:
46
+ """دستورالعمل سیستمی"""
47
+ return """You are a Persian financial/news text anonymizer. Replace entities with indexed placeholders.
48
+
49
+ ## Rules:
50
+ 1. **Sequential indexing**: company-01, company-02... | person-01, person-02... | amount-01, amount-02... | percent-01, percent-02...
51
+ 2. **Consistency**: Same entity = same index throughout text
52
+ 3. **Preserve**: dates, times, units (میلیارد، میلیون، تومان، درصد)
53
+ 4. **Keep generic words**: "سه شرکت" stays as is (don't replace numbers in generic phrases)
54
+
55
+ ## Entity types:
56
+ - **company-XX**: شرکت‌ها، بانک‌ها، سازمان‌ها
57
+ - **person-XX**: نام و نام خانوادگی
58
+ - **amount-XX**: مبالغ مالی (with units)
59
+ - **percent-XX**: درصدها (with %)
60
+
61
+ ## Examples:
62
+
63
+ Input: همراه اول با سهمی ۵۳ درصدی بیشترین نقش را دارد.
64
+ Output: company-01 با سهمی percent-01 بیشترین نقش را دارد.
65
+
66
+ Input: ایران خودرو حدود 23 هزار میلیارد درآمد کسب کرد.
67
+ Output: company-01 حدود amount-01 درآمد کسب کرد.
68
+
69
+ Input: علی محمدی مدیرعامل بانک ملت گفت سود 15 درصدی داشتند.
70
+ Output: person-01 مدیرعامل company-01 گفت سود percent-01 داشتند.
71
+
72
+ ⚠️ Return ONLY the anonymized Persian text, no explanations."""
73
+
74
+ def _try_model(self, model: str, text: str) -> Optional[Dict[str, Any]]:
75
+ """تست یک مدل"""
76
  headers = {
77
+ "Authorization": f"Bearer {self.config.api_key}",
78
  "Content-Type": "application/json"
79
  }
80
 
81
  payload = {
82
  "model": model,
83
  "messages": [
84
+ {"role": "system", "content": self.system_prompt},
85
+ {"role": "user", "content": f"Anonymize this Persian text:\n\n{text}"}
86
  ],
87
+ "temperature": self.config.temperature,
88
+ "top_p": self.config.top_p,
89
+ "max_tokens": self.config.max_tokens
90
  }
91
 
92
  try:
93
+ response = requests.post(
94
+ f"{self.config.base_url}/chat/completions",
95
+ headers=headers,
96
+ json=payload,
97
+ timeout=60
98
+ )
99
 
100
  if response.status_code == 200:
101
+ return response.json()
 
 
 
 
 
 
102
  else:
103
+ print(f"⚠️ {model}: status {response.status_code}")
104
+ return None
105
+
 
 
 
 
 
106
  except Exception as e:
107
+ print(f"⚠️ {model}: {str(e)}")
108
+ return None
109
 
110
+ def _make_api_request(self, text: str) -> Dict[str, Any]:
111
+ """ارسال درخواست با fallback"""
112
+
113
+ # ابتدا مدل پیش‌فرض را امتحان کن
114
+ if self.working_model:
115
+ result = self._try_model(self.working_model, text)
116
+ if result:
117
+ return result
118
+
119
+ # امتحان مدل پیش‌فرض
120
+ print(f"🔄 Using: {self.config.model}")
121
+ result = self._try_model(self.config.model, text)
122
+ if result:
123
+ self.working_model = self.config.model
124
+ return result
125
+
126
+ # fallback به مدل‌های دیگر
127
+ print("🔍 Trying alternatives...")
128
+ for model in self.AVAILABLE_MODELS:
129
+ if model != self.config.model:
130
+ print(f"⏳ Trying {model}...")
131
+ result = self._try_model(model, text)
132
+ if result:
133
+ print(f"✅ Success with {model}")
134
+ self.working_model = model
135
+ return result
136
+
137
+ raise Exception(
138
+ "هیچ مدلی کار نکرد. لطفاً بررسی کنید:\n"
139
+ "1. کلید API معتبر است\n"
140
+ "2. اتصال اینترنت فعال است\n"
141
+ "3. OpenRouter سرویس‌دهی می‌کند"
142
+ )
143
 
144
+ def anonymize_text(self, text: str) -> Dict[str, Any]:
145
+ """ناشناس‌سازی متن"""
146
+ if not text.strip():
147
+ return {"success": False, "error": "متن ورودی خالی است"}
148
+
149
+ try:
150
+ response = self._make_api_request(text)
151
+
152
+ if "choices" not in response or not response["choices"]:
153
+ return {"success": False, "error": "پاسخ نامعتبر"}
154
+
155
+ content = response["choices"][0]["message"]["content"]
156
+
157
+ # پاک‌سازی
158
+ content = self._clean_output(content)
159
+
160
+ # تحلیل
161
+ analysis = self._analyze_anonymized_text(content)
162
+
163
+ return {
164
+ "success": True,
165
+ "anonymized_text": content,
166
+ "entities": analysis["entities"],
167
+ "statistics": analysis["statistics"],
168
+ "usage": response.get("usage", {}),
169
+ "model_used": self.working_model or self.config.model
170
+ }
171
+
172
+ except Exception as e:
173
+ return {"success": False, "error": str(e)}
174
 
175
+ def _clean_output(self, content: str) -> str:
176
+ """پاک‌سازی خروجی"""
177
+ # حذف markdown
178
+ if "```" in content:
179
+ lines = content.split('\n')
180
+ clean_lines = []
181
+ skip = False
182
+ for line in lines:
183
+ if line.strip().startswith('```'):
184
+ skip = not skip
185
+ continue
186
+ if not skip:
187
+ clean_lines.append(line)
188
+ content = '\n'.join(clean_lines)
189
+
190
+ # حذف توضیحات اضافی
191
+ lines = content.split('\n')
192
+ persian_lines = []
193
+ for line in lines:
194
+ # فقط خطوطی که فارسی یا entity-XX دارند
195
+ if any(c in 'ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهی' for c in line) or \
196
+ re.search(r'(company|person|amount|percent)-\d+', line):
197
+ persian_lines.append(line)
198
+
199
+ return '\n'.join(persian_lines).strip()
200
 
201
+ def _analyze_anonymized_text(self, text: str) -> Dict[str, Any]:
202
+ """تحلیل متن"""
203
+ companies = re.findall(r'company-(\d+)', text)
204
+ persons = re.findall(r'person-(\d+)', text)
205
+ amounts = re.findall(r'amount-(\d+)', text)
206
+ percents = re.findall(r'percent-(\d+)', text)
207
+
208
+ return {
209
+ "statistics": {
210
+ "company": len(set(companies)),
211
+ "person": len(set(persons)),
212
+ "amount": len(set(amounts)),
213
+ "percent": len(set(percents)),
214
+ "total": len(companies) + len(persons) + len(amounts) + len(percents)
215
+ },
216
+ "entities": {
217
+ "companies": sorted(list(set(companies)), key=lambda x: int(x)) if companies else [],
218
+ "persons": sorted(list(set(persons)), key=lambda x: int(x)) if persons else [],
219
+ "amounts": sorted(list(set(amounts)), key=lambda x: int(x)) if amounts else [],
220
+ "percents": sorted(list(set(percents)), key=lambda x: int(x)) if percents else []
221
+ }
222
  }
223
+
224
+ def create_interface():
225
+ """رابط کاربری Gradio"""
226
+
227
+ api_key_available = bool(os.getenv("OPENROUTER_API_KEY"))
228
+
229
+ with gr.Blocks(
230
+ title="ناشناس‌ساز فارسی",
231
+ theme=gr.themes.Soft(),
232
+ css=".rtl { direction: rtl; text-align: right; }"
233
+ ) as interface:
234
 
235
+ gr.Markdown("""
236
+ # 🔒 ناشناس‌ساز متون مالی/خبری فارسی
237
+ ### 🚀 Powered by Llama 3.2 (OpenRouter Free)
238
+ """)
 
239
 
240
+ if not api_key_available:
241
+ gr.Markdown("""
242
+ ## ⚠️ نیاز به تنظیمات
243
 
244
+ 1. به https://openrouter.ai/keys بروید
245
+ 2. یک کلید API رایگان بگیرید
246
+ 3. در Hugging Face:
247
+ - Settings → Secrets
248
+ - New Secret
249
+ - Name: `OPENROUTER_API_KEY`
250
+ - Value: کلید شما
251
+ 4. Space را Restart کنید
252
+ """)
253
  else:
254
+ gr.Markdown(" **سیستم آماده به کار است!**")
255
+
256
+ with gr.Row():
257
+ with gr.Column():
258
+ input_text = gr.Textbox(
259
+ label="📝 متن ورودی",
260
+ placeholder="متن خبری یا مالی خود را اینجا بنویسید...",
261
+ lines=12,
262
+ elem_classes="rtl"
263
+ )
264
+
265
+ with gr.Row():
266
+ clear_btn = gr.Button("🗑️ پاک کردن", size="sm")
267
+ anonymize_btn = gr.Button("🔒 ناشناس‌سازی", variant="primary", size="lg")
268
+
269
+ with gr.Column():
270
+ output_text = gr.Textbox(
271
+ label="🎯 متن ناشناس‌سازی شده",
272
+ lines=12,
273
+ elem_classes="rtl"
274
+ )
275
+
276
+ copy_btn = gr.Button("📋 کپی خروجی", size="sm")
277
+
278
+ info_output = gr.Markdown(label="📊 اطلاعات")
279
+
280
+ def process_text(text: str):
281
+ """پردازش متن"""
282
+ if not text or not text.strip():
283
+ return "", "❌ لطفاً متنی وارد کنید"
284
+
285
+ try:
286
+ anonymizer = AdvancedOpenRouterAnonymizer()
287
+ result = anonymizer.anonymize_text(text)
288
+
289
+ if not result["success"]:
290
+ return "", f"❌ **خطا:**\n\n{result['error']}"
291
+
292
+ stats = result.get("statistics", {})
293
+ usage = result.get("usage", {})
294
+ model = result.get("model_used", "unknown")
295
+ entities = result.get("entities", {})
296
+
297
+ # آمار جزئی
298
+ entities_detail = []
299
+ if entities.get("companies"):
300
+ entities_detail.append(f"🏢 شرکت‌ها: {', '.join([f'company-{i}' for i in entities['companies']])}")
301
+ if entities.get("persons"):
302
+ entities_detail.append(f"👤 اشخاص: {', '.join([f'person-{i}' for i in entities['persons']])}")
303
+ if entities.get("amounts"):
304
+ entities_detail.append(f"💰 مبالغ: {', '.join([f'amount-{i}' for i in entities['amounts']])}")
305
+ if entities.get("percents"):
306
+ entities_detail.append(f"📊 درصدها: {', '.join([f'percent-{i}' for i in entities['percents']])}")
307
+
308
+ entities_text = "\n- ".join(entities_detail) if entities_detail else "هیچ موجودیتی یافت نشد"
309
+
310
+ info_md = f"""
311
+ ## ✅ ناشناس‌سازی موفق!
312
+
313
+ ### 📊 خلاصه آمار:
314
+ - **🏢 شرکت‌ها:** {stats.get('company', 0)}
315
+ - **👤 اشخاص:** {stats.get('person', 0)}
316
+ - **💰 مبالغ:** {stats.get('amount', 0)}
317
+ - **📊 درصدها:** {stats.get('percent', 0)}
318
+ - **🔢 کل جایگزینی‌ها:** {stats.get('total', 0)}
319
+
320
+ ### 🔍 موجودیت‌های شناسایی شده:
321
+ - {entities_text}
322
+
323
+ ### ⚙️ تنظیمات فنی:
324
+ - 🤖 **مدل:** `{model}`
325
+ - 📊 **Tokens:** {usage.get('total_tokens', 0)} (ورودی: {usage.get('prompt_tokens', 0)}, خروجی: {usage.get('completion_tokens', 0)})
326
+ - 🆓 **هزینه:** رایگان
327
+ """
328
+
329
+ return result["anonymized_text"], info_md
330
+
331
+ except Exception as e:
332
+ return "", f"❌ **خطای غیرمنتظره:**\n\n```\n{str(e)}\n```"
333
+
334
+ def clear_all():
335
+ return "", "", ""
336
+
337
+ # Event handlers
338
+ anonymize_btn.click(
339
+ fn=process_text,
340
+ inputs=[input_text],
341
+ outputs=[output_text, info_output]
342
+ )
343
+
344
+ clear_btn.click(
345
+ fn=clear_all,
346
+ outputs=[input_text, output_text, info_output]
347
+ )
348
+
349
+ # مثال‌ها
350
+ with gr.Accordion("📚 مثال‌های آماده", open=False):
351
+ gr.Examples(
352
+ examples=[
353
+ ["همراه اول با سهمی ۵۳ درصدی بیشترین نقش را در بازار دارد."],
354
+ ["ایران خودرو در اسفند 1402 حدود 23 هزار و 296 میلیارد تومان درآمد کسب کرد که 4.58 درصد افزایش نسبت به سال قبل داشت."],
355
+ ["علی محمدی مدیرعامل بانک ملت اعلام کرد که این بانک سود 15 درصدی داشته و 500 میلیون تومان سرمایه‌گذاری جدید انجام داده است."],
356
+ ["فولاد مبارکه اصفهان با نماد فاما در بورس، رشد 8.5 درصدی قیمت سهام را تجربه کرد و به ارزش 12 هزار میلیارد تومان رسید."]
357
+ ],
358
+ inputs=input_text,
359
+ label="مثال‌ها - روی هر کدام کلیک کنید"
360
+ )
361
+
362
+ # راهنما
363
+ with gr.Accordion("📖 راهنمای استفاده", open=False):
364
+ gr.Markdown("""
365
+ ## چگونه استفاده کنیم؟
366
+
367
+ 1. **متن ورودی را وارد کنید**: متن خبری یا مالی فارسی
368
+ 2. **کلیک روی ناشناس‌سازی**: سیستم خودکار موجودیت‌ها را شناسایی می‌کند
369
+ 3. **مشاهده نتیجه**: متن ناشناس‌سازی شده + آمار کامل
370
+
371
+ ## انواع موجودیت‌ها:
372
+ - **company-XX**: نام شرکت‌ها، بانک‌ها، سازمان‌ها
373
+ - **person-XX**: نام و نام خانوادگی افراد
374
+ - **amount-XX**: مبالغ مالی (با حفظ واحد)
375
+ - **percent-XX**: اعداد درصد (با حفظ علامت %)
376
+
377
+ ## نکات مهم:
378
+ - ✅ تاریخ‌ها و زمان‌ها حفظ می‌شوند
379
+ - ✅ واحدهای پولی (تومان، میلیارد، ...) حفظ می‌شوند
380
+ - ✅ اعداد عمومی (مثل "سه شرکت") تغییر نمی‌کنند
381
+ - ✅ اندیس‌گذاری از 01 شروع می‌شود و پیوسته است
382
+ """)
383
 
384
+ return interface
 
385
 
386
  if __name__ == "__main__":
387
+ interface = create_interface()
388
+ interface.launch()