minh9972t12 commited on
Commit
295cedb
·
verified ·
1 Parent(s): cfef899

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -27
app.py CHANGED
@@ -38,6 +38,42 @@ else:
38
  print("⚠ Warning: No HUGGINGFACE_TOKEN found. Set it in environment variable.")
39
 
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  # Pydantic models
42
  class ContentValidationResult(BaseModel):
43
  is_valid: bool
@@ -94,6 +130,118 @@ async def root():
94
  }
95
 
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  def build_validation_prompt(
98
  event_name: str,
99
  category: str,
@@ -153,10 +301,39 @@ async def validate_content(
153
  token: str
154
  ) -> ContentValidationResult:
155
  """
156
- Validate content using LLM
157
  """
158
 
159
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  # Build validation prompt
161
  prompt = build_validation_prompt(
162
  event_name=event_name,
@@ -168,23 +345,48 @@ async def validate_content(
168
  # Initialize client
169
  client = InferenceClient(token=token)
170
 
171
- # Use Mistral for fast validation
172
- print("🔍 Validating content with Mistral-7B-Instruct-v0.3...")
173
-
174
  messages = [{"role": "user", "content": prompt}]
175
 
176
- response = client.chat_completion(
177
- messages=messages,
178
- model="mistralai/Mistral-7B-Instruct-v0.3",
179
- max_tokens=500,
180
- temperature=0.1, # Very low temperature for consistent JSON
181
- top_p=0.9
182
- )
 
 
183
 
184
- llm_response = response.choices[0].message.content.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  print(f"\n{'='*60}")
187
- print(f"VALIDATION RESPONSE:")
188
  print(f"{'='*60}")
189
  print(llm_response)
190
  print(f"{'='*60}\n")
@@ -216,14 +418,14 @@ async def validate_content(
216
  print(f"⚠ JSON Parse Error: {str(parse_error)}")
217
  print(f"Response was: {llm_response[:200]}")
218
 
219
- # Fallback: assume valid if can't parse
220
- data = {
221
- "is_valid": True,
222
- "confidence_score": 0.5,
223
- "reason": "Không thể parse validation response, cho phép qua",
224
- "issues": [],
225
- "suggestions": []
226
- }
227
 
228
  return ContentValidationResult(
229
  is_valid=data.get("is_valid", True),
@@ -235,13 +437,13 @@ async def validate_content(
235
 
236
  except Exception as e:
237
  print(f"⚠ Validation error: {str(e)}")
238
- # On error, allow content but with warning
239
  return ContentValidationResult(
240
- is_valid=True,
241
- confidence_score=0.5,
242
- reason=f"Lỗi validation: {str(e)}. Cho phép qua mặc định.",
243
- issues=[],
244
- suggestions=[]
245
  )
246
 
247
 
 
38
  print("⚠ Warning: No HUGGINGFACE_TOKEN found. Set it in environment variable.")
39
 
40
 
41
+ # Vietnamese profanity/offensive words list (expandable)
42
+ VIETNAMESE_PROFANITY = [
43
+ "đjt", "địt", "đm", "dm", "đéo", "đệch", "vl", "vcl", "cc", "cặc",
44
+ "lồn", "buồi", "đụ", "chó", "súc vật", "con chó", "thằng chó",
45
+ "con đĩ", "đĩ", "điếm", "cave", "gái gọi", "mẹ mày", "bố mày",
46
+ "cha mày", "cụ mày", "óc chó", "não chó", "não lợn", "ngu như chó",
47
+ "chết mẹ", "chết cha", "đồ khốn", "thằng khốn", "con khốn"
48
+ ]
49
+
50
+ # Spam patterns
51
+ SPAM_PATTERNS = [
52
+ r'(\w)\1{4,}', # Repeated characters: "aaaa", "!!!!!"
53
+ r'(\.{3,}|!{3,}|\?{3,}|\${3,}|\*{3,})', # Excessive punctuation
54
+ r'\d{9,}', # Long numbers (phone numbers)
55
+ r'(http|www)\S+', # URLs
56
+ r'(\w+\s+){0,3}(mua|bán|giảm giá|khuyến mãi|liên hệ|zalo|telegram)\s+\d', # Sales spam
57
+ ]
58
+
59
+ # Gibberish patterns
60
+ GIBBERISH_PATTERNS = [
61
+ r'^[a-z]{20,}$', # Very long random lowercase string
62
+ r'(qwerty|asdfgh|zxcvbn|123456|abcdef)', # Keyboard patterns
63
+ r'[a-z]{5,}[0-9]{5,}', # Mixed random: "asdfg12345"
64
+ ]
65
+
66
+ # Bypass attempt patterns
67
+ BYPASS_PATTERNS = [
68
+ r'ignore\s+(previous|above|all)\s+(instruction|prompt|rule)',
69
+ r'you\s+are\s+(now|a|an)\s+',
70
+ r'act\s+as\s+',
71
+ r'<script|<iframe|javascript:|onerror=',
72
+ r'(SELECT|INSERT|DELETE|DROP|UPDATE)\s+.*FROM',
73
+ r'system\s*\(|exec\s*\(|eval\s*\(',
74
+ ]
75
+
76
+
77
  # Pydantic models
78
  class ContentValidationResult(BaseModel):
79
  is_valid: bool
 
130
  }
131
 
132
 
133
+ def check_profanity_vietnamese(text: str) -> tuple[bool, List[str]]:
134
+ """
135
+ Check for Vietnamese profanity using word list
136
+ Returns (has_profanity, found_words)
137
+ """
138
+ text_lower = text.lower()
139
+ found = []
140
+
141
+ for word in VIETNAMESE_PROFANITY:
142
+ # Check for exact word boundaries
143
+ pattern = r'\b' + re.escape(word) + r'\b'
144
+ if re.search(pattern, text_lower):
145
+ found.append(word)
146
+
147
+ return len(found) > 0, found
148
+
149
+
150
+ def check_spam_patterns(text: str) -> tuple[bool, List[str]]:
151
+ """
152
+ Check for spam patterns
153
+ Returns (is_spam, issues)
154
+ """
155
+ issues = []
156
+
157
+ for pattern in SPAM_PATTERNS:
158
+ matches = re.findall(pattern, text, re.IGNORECASE)
159
+ if matches:
160
+ issues.append(f"Spam pattern detected: {pattern[:30]}...")
161
+
162
+ return len(issues) > 0, issues
163
+
164
+
165
+ def check_gibberish(text: str) -> tuple[bool, List[str]]:
166
+ """
167
+ Check for gibberish patterns
168
+ Returns (is_gibberish, issues)
169
+ """
170
+ issues = []
171
+
172
+ for pattern in GIBBERISH_PATTERNS:
173
+ if re.search(pattern, text, re.IGNORECASE):
174
+ issues.append(f"Gibberish pattern detected")
175
+
176
+ # Check for very low vowel ratio (Vietnamese needs vowels)
177
+ vowels = len(re.findall(r'[aeiouàáảãạăằắẳẵặâầấẩẫậèéẻẽẹêềếểễệìíỉĩịòóỏõọôồốổỗộơờớởỡợùúủũụưừứửữựỳýỷỹỵ]', text.lower()))
178
+ consonants = len(re.findall(r'[bcdfghjklmnpqrstvwxyz]', text.lower()))
179
+
180
+ if consonants > 10 and vowels / (consonants + vowels) < 0.3:
181
+ issues.append("Low vowel ratio - possibly gibberish")
182
+
183
+ return len(issues) > 0, issues
184
+
185
+
186
+ def check_bypass_attempts(text: str) -> tuple[bool, List[str]]:
187
+ """
188
+ Check for bypass/injection attempts
189
+ Returns (is_bypass, issues)
190
+ """
191
+ issues = []
192
+
193
+ for pattern in BYPASS_PATTERNS:
194
+ if re.search(pattern, text, re.IGNORECASE):
195
+ issues.append(f"Bypass attempt detected")
196
+
197
+ return len(issues) > 0, issues
198
+
199
+
200
+ def rule_based_validation(
201
+ event_name: str,
202
+ category: str,
203
+ short_desc: str,
204
+ detailed_desc: str
205
+ ) -> tuple[bool, float, str, List[str]]:
206
+ """
207
+ Rule-based validation before LLM
208
+ Returns (is_valid, confidence, reason, issues)
209
+ """
210
+
211
+ all_text = f"{event_name} {category} {short_desc} {detailed_desc}"
212
+ issues = []
213
+
214
+ # Check profanity
215
+ has_profanity, profane_words = check_profanity_vietnamese(all_text)
216
+ if has_profanity:
217
+ issues.append(f"Phát hiện từ ngữ tục tĩu: {', '.join(profane_words[:3])}")
218
+
219
+ # Check spam
220
+ is_spam, spam_issues = check_spam_patterns(all_text)
221
+ if is_spam:
222
+ issues.extend(spam_issues)
223
+
224
+ # Check gibberish
225
+ is_gibberish, gibberish_issues = check_gibberish(all_text)
226
+ if is_gibberish:
227
+ issues.extend(gibberish_issues)
228
+
229
+ # Check bypass attempts
230
+ is_bypass, bypass_issues = check_bypass_attempts(all_text)
231
+ if is_bypass:
232
+ issues.extend(bypass_issues)
233
+
234
+ # Determine validity
235
+ if has_profanity or is_bypass:
236
+ return False, 0.1, "Nội dung vi phạm: chứa từ ngữ không phù hợp hoặc cố gắng bypass", issues
237
+ elif is_spam:
238
+ return False, 0.3, "Nội dung có dấu hiệu spam", issues
239
+ elif is_gibberish:
240
+ return False, 0.2, "Nội dung có dấu hiệu vô nghĩa (gibberish)", issues
241
+
242
+ return True, 0.8, "Nội dung hợp lệ (rule-based check)", []
243
+
244
+
245
  def build_validation_prompt(
246
  event_name: str,
247
  category: str,
 
301
  token: str
302
  ) -> ContentValidationResult:
303
  """
304
+ Validate content using Rule-Based + LLM hybrid approach
305
  """
306
 
307
  try:
308
+ # STEP 1: Rule-based validation (fast, accurate for common cases)
309
+ print("🔍 Step 1: Rule-based validation...")
310
+ is_valid_rule, confidence_rule, reason_rule, issues_rule = rule_based_validation(
311
+ event_name=event_name,
312
+ category=category,
313
+ short_desc=short_desc,
314
+ detailed_desc=detailed_desc
315
+ )
316
+
317
+ # If rule-based catches issues, return immediately
318
+ if not is_valid_rule:
319
+ print(f"❌ Rule-based validation FAILED: {reason_rule}")
320
+ return ContentValidationResult(
321
+ is_valid=False,
322
+ confidence_score=confidence_rule,
323
+ reason=reason_rule,
324
+ issues=issues_rule,
325
+ suggestions=[
326
+ "Loại bỏ các từ ngữ không phù hợp",
327
+ "Sử dụng ngôn ngữ lịch sự và chuyên nghiệp",
328
+ "Đảm bảo nội dung liên quan đến sự kiện"
329
+ ]
330
+ )
331
+
332
+ print("✓ Rule-based validation PASSED")
333
+
334
+ # STEP 2: LLM validation (for nuanced cases)
335
+ print("🔍 Step 2: LLM validation with Qwen2.5-7B-Instruct...")
336
+
337
  # Build validation prompt
338
  prompt = build_validation_prompt(
339
  event_name=event_name,
 
345
  # Initialize client
346
  client = InferenceClient(token=token)
347
 
 
 
 
348
  messages = [{"role": "user", "content": prompt}]
349
 
350
+ # Try multiple models in order of preference
351
+ models_to_try = [
352
+ "Qwen/Qwen2.5-7B-Instruct", # Best for Vietnamese
353
+ "google/gemma-2-2b-it", # Good JSON adherence
354
+ "mistralai/Mistral-7B-Instruct-v0.3", # Fallback
355
+ ]
356
+
357
+ llm_response = None
358
+ model_used = None
359
 
360
+ for model in models_to_try:
361
+ try:
362
+ print(f" Trying {model}...")
363
+ response = client.chat_completion(
364
+ messages=messages,
365
+ model=model,
366
+ max_tokens=500,
367
+ temperature=0.1,
368
+ top_p=0.9
369
+ )
370
+ llm_response = response.choices[0].message.content.strip()
371
+ model_used = model
372
+ print(f" ✓ Success with {model}")
373
+ break
374
+ except Exception as e:
375
+ print(f" ✗ Failed with {model}: {str(e)[:100]}")
376
+ continue
377
+
378
+ if not llm_response:
379
+ print("⚠ All LLM models failed, using rule-based result")
380
+ return ContentValidationResult(
381
+ is_valid=is_valid_rule,
382
+ confidence_score=confidence_rule,
383
+ reason=reason_rule + " (LLM unavailable)",
384
+ issues=issues_rule,
385
+ suggestions=[]
386
+ )
387
 
388
  print(f"\n{'='*60}")
389
+ print(f"VALIDATION RESPONSE ({model_used}):")
390
  print(f"{'='*60}")
391
  print(llm_response)
392
  print(f"{'='*60}\n")
 
418
  print(f"⚠ JSON Parse Error: {str(parse_error)}")
419
  print(f"Response was: {llm_response[:200]}")
420
 
421
+ # Fallback to rule-based result
422
+ return ContentValidationResult(
423
+ is_valid=is_valid_rule,
424
+ confidence_score=confidence_rule,
425
+ reason=reason_rule + " (LLM parse failed)",
426
+ issues=issues_rule,
427
+ suggestions=[]
428
+ )
429
 
430
  return ContentValidationResult(
431
  is_valid=data.get("is_valid", True),
 
437
 
438
  except Exception as e:
439
  print(f"⚠ Validation error: {str(e)}")
440
+ # On error, deny content to be safe
441
  return ContentValidationResult(
442
+ is_valid=False,
443
+ confidence_score=0.3,
444
+ reason=f"Lỗi validation: {str(e)}. Từ chối để đảm bảo an toàn.",
445
+ issues=[str(e)],
446
+ suggestions=["Vui lòng thử lại hoặc liên hệ support"]
447
  )
448
 
449