Spaces:
Runtime error
Runtime error
Mustafa Öztürk commited on
Commit ·
b2e6438
1
Parent(s): ddd2daa
Add early-exit and Detoxify call counters to moderation logs
Browse files
app/services/moderation_service.py
CHANGED
|
@@ -64,6 +64,12 @@ ACTION_MAP = {
|
|
| 64 |
}
|
| 65 |
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
def _extract_blacklist_hits(cleaned_text: str, dil: str):
|
| 68 |
pure_text = re.sub(r"[^a-zA-ZçğıöşüÇĞİÖŞÜ0-9\s]", "", cleaned_text).lower()
|
| 69 |
words_in_pure_text = set(pure_text.split())
|
|
@@ -125,8 +131,10 @@ def _tr_off_scores_batched(text_list, state, batch_size: int = 8):
|
|
| 125 |
|
| 126 |
def _tr_result_with_off_score(cleaned_text: str, profanity_hits, insult_hits, off_score: float, state, dil: str):
|
| 127 |
# Only run Detoxify on uncertain content to reduce inference cost.
|
|
|
|
| 128 |
if off_score < 0.60:
|
| 129 |
raw_threat_res = state["D_MULTI"].predict(cleaned_text)
|
|
|
|
| 130 |
else:
|
| 131 |
raw_threat_res = {
|
| 132 |
"toxicity": off_score,
|
|
@@ -158,16 +166,20 @@ def _tr_result_with_off_score(cleaned_text: str, profanity_hits, insult_hits, of
|
|
| 158 |
},
|
| 159 |
)
|
| 160 |
detail.update({"action": ACTION_MAP.get(verdict["risk_level"], "MONITOR")})
|
| 161 |
-
return verdict["decision"], verdict["reason"], verdict["risk_level"], dil, cleaned_text, detail
|
| 162 |
|
| 163 |
|
| 164 |
def run_moderation(text: str, platform_dil: str = "tr"):
|
| 165 |
state = _ensure_runtime_ready()
|
|
|
|
|
|
|
| 166 |
|
| 167 |
temiz = clean_text_nfkc(text)
|
| 168 |
dil = "en" if platform_dil == "en" else "tr"
|
| 169 |
|
| 170 |
if is_spam(temiz, dil):
|
|
|
|
|
|
|
| 171 |
return (
|
| 172 |
"🗑️ SPAM/GİBBERİSH",
|
| 173 |
"Anlamsız veya tekrarlı içerik.",
|
|
@@ -181,6 +193,8 @@ def run_moderation(text: str, platform_dil: str = "tr"):
|
|
| 181 |
|
| 182 |
# Fast path: if blacklist catches profanity/insult, skip all ML inference.
|
| 183 |
if profanity_hits or insult_hits:
|
|
|
|
|
|
|
| 184 |
return _blacklist_early_result(profanity_hits, insult_hits, dil, temiz)
|
| 185 |
|
| 186 |
if dil == "en":
|
|
@@ -191,6 +205,8 @@ def run_moderation(text: str, platform_dil: str = "tr"):
|
|
| 191 |
"score": float(gb_raw.get("score", 0.0)),
|
| 192 |
}
|
| 193 |
if gb.get("label", "").lower() == "noise" and gb.get("score", 0.0) > 0.98:
|
|
|
|
|
|
|
| 194 |
return (
|
| 195 |
"🗑️ GIBBERISH/SPAM",
|
| 196 |
"Metin anlamsız karakter dizileri içeriyor.",
|
|
@@ -201,6 +217,7 @@ def run_moderation(text: str, platform_dil: str = "tr"):
|
|
| 201 |
)
|
| 202 |
|
| 203 |
raw_res = state["D_EN"].predict(temiz)
|
|
|
|
| 204 |
res = {k: float(v) for k, v in raw_res.items()}
|
| 205 |
tox_score = float(res.get("toxicity", 0.0))
|
| 206 |
ins_score = float(res.get("insult", 0.0))
|
|
@@ -223,15 +240,22 @@ def run_moderation(text: str, platform_dil: str = "tr"):
|
|
| 223 |
},
|
| 224 |
)
|
| 225 |
detail.update({"action": ACTION_MAP.get(verdict["risk_level"], "MONITOR")})
|
|
|
|
| 226 |
return verdict["decision"], verdict["reason"], verdict["risk_level"], dil, temiz, detail
|
| 227 |
|
| 228 |
off_score = _tr_off_scores_batched([temiz], state, batch_size=1)[0]
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
|
| 231 |
|
| 232 |
def run_moderation_batch(texts, platform_dil: str = "tr", batch_size: int = 8):
|
| 233 |
state = _ensure_runtime_ready()
|
| 234 |
dil = "en" if platform_dil == "en" else "tr"
|
|
|
|
|
|
|
| 235 |
|
| 236 |
results = [None] * len(texts)
|
| 237 |
tr_pending = []
|
|
@@ -241,6 +265,7 @@ def run_moderation_batch(texts, platform_dil: str = "tr", batch_size: int = 8):
|
|
| 241 |
temiz = clean_text_nfkc(text)
|
| 242 |
|
| 243 |
if is_spam(temiz, dil):
|
|
|
|
| 244 |
results[idx] = (
|
| 245 |
"🗑️ SPAM/GİBBERİSH",
|
| 246 |
"Anlamsız veya tekrarlı içerik.",
|
|
@@ -253,11 +278,55 @@ def run_moderation_batch(texts, platform_dil: str = "tr", batch_size: int = 8):
|
|
| 253 |
|
| 254 |
profanity_hits, insult_hits = _extract_blacklist_hits(temiz, dil)
|
| 255 |
if profanity_hits or insult_hits:
|
|
|
|
| 256 |
results[idx] = _blacklist_early_result(profanity_hits, insult_hits, dil, temiz)
|
| 257 |
continue
|
| 258 |
|
| 259 |
if dil == "en":
|
| 260 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
continue
|
| 262 |
|
| 263 |
tr_pending.append((idx, temiz, profanity_hits, insult_hits))
|
|
@@ -267,6 +336,11 @@ def run_moderation_batch(texts, platform_dil: str = "tr", batch_size: int = 8):
|
|
| 267 |
off_scores = _tr_off_scores_batched(tr_pending_texts, state, batch_size=batch_size)
|
| 268 |
for pending, off_score in zip(tr_pending, off_scores):
|
| 269 |
idx, temiz, profanity_hits, insult_hits = pending
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
|
| 272 |
return results
|
|
|
|
| 64 |
}
|
| 65 |
|
| 66 |
|
| 67 |
+
def _log_pipeline_counts(early_exit_count: int, detoxify_call_count: int):
|
| 68 |
+
print(f"Early exit: {early_exit_count}")
|
| 69 |
+
print(f"Detoxify çağrısı: {detoxify_call_count}")
|
| 70 |
+
print(f"CUDA aktif: {torch.cuda.is_available()}")
|
| 71 |
+
|
| 72 |
+
|
| 73 |
def _extract_blacklist_hits(cleaned_text: str, dil: str):
|
| 74 |
pure_text = re.sub(r"[^a-zA-ZçğıöşüÇĞİÖŞÜ0-9\s]", "", cleaned_text).lower()
|
| 75 |
words_in_pure_text = set(pure_text.split())
|
|
|
|
| 131 |
|
| 132 |
def _tr_result_with_off_score(cleaned_text: str, profanity_hits, insult_hits, off_score: float, state, dil: str):
|
| 133 |
# Only run Detoxify on uncertain content to reduce inference cost.
|
| 134 |
+
detoxify_called = False
|
| 135 |
if off_score < 0.60:
|
| 136 |
raw_threat_res = state["D_MULTI"].predict(cleaned_text)
|
| 137 |
+
detoxify_called = True
|
| 138 |
else:
|
| 139 |
raw_threat_res = {
|
| 140 |
"toxicity": off_score,
|
|
|
|
| 166 |
},
|
| 167 |
)
|
| 168 |
detail.update({"action": ACTION_MAP.get(verdict["risk_level"], "MONITOR")})
|
| 169 |
+
return (verdict["decision"], verdict["reason"], verdict["risk_level"], dil, cleaned_text, detail), detoxify_called
|
| 170 |
|
| 171 |
|
| 172 |
def run_moderation(text: str, platform_dil: str = "tr"):
|
| 173 |
state = _ensure_runtime_ready()
|
| 174 |
+
early_exit_count = 0
|
| 175 |
+
detoxify_call_count = 0
|
| 176 |
|
| 177 |
temiz = clean_text_nfkc(text)
|
| 178 |
dil = "en" if platform_dil == "en" else "tr"
|
| 179 |
|
| 180 |
if is_spam(temiz, dil):
|
| 181 |
+
early_exit_count += 1
|
| 182 |
+
_log_pipeline_counts(early_exit_count, detoxify_call_count)
|
| 183 |
return (
|
| 184 |
"🗑️ SPAM/GİBBERİSH",
|
| 185 |
"Anlamsız veya tekrarlı içerik.",
|
|
|
|
| 193 |
|
| 194 |
# Fast path: if blacklist catches profanity/insult, skip all ML inference.
|
| 195 |
if profanity_hits or insult_hits:
|
| 196 |
+
early_exit_count += 1
|
| 197 |
+
_log_pipeline_counts(early_exit_count, detoxify_call_count)
|
| 198 |
return _blacklist_early_result(profanity_hits, insult_hits, dil, temiz)
|
| 199 |
|
| 200 |
if dil == "en":
|
|
|
|
| 205 |
"score": float(gb_raw.get("score", 0.0)),
|
| 206 |
}
|
| 207 |
if gb.get("label", "").lower() == "noise" and gb.get("score", 0.0) > 0.98:
|
| 208 |
+
early_exit_count += 1
|
| 209 |
+
_log_pipeline_counts(early_exit_count, detoxify_call_count)
|
| 210 |
return (
|
| 211 |
"🗑️ GIBBERISH/SPAM",
|
| 212 |
"Metin anlamsız karakter dizileri içeriyor.",
|
|
|
|
| 217 |
)
|
| 218 |
|
| 219 |
raw_res = state["D_EN"].predict(temiz)
|
| 220 |
+
detoxify_call_count += 1
|
| 221 |
res = {k: float(v) for k, v in raw_res.items()}
|
| 222 |
tox_score = float(res.get("toxicity", 0.0))
|
| 223 |
ins_score = float(res.get("insult", 0.0))
|
|
|
|
| 240 |
},
|
| 241 |
)
|
| 242 |
detail.update({"action": ACTION_MAP.get(verdict["risk_level"], "MONITOR")})
|
| 243 |
+
_log_pipeline_counts(early_exit_count, detoxify_call_count)
|
| 244 |
return verdict["decision"], verdict["reason"], verdict["risk_level"], dil, temiz, detail
|
| 245 |
|
| 246 |
off_score = _tr_off_scores_batched([temiz], state, batch_size=1)[0]
|
| 247 |
+
result, detoxify_called = _tr_result_with_off_score(temiz, profanity_hits, insult_hits, off_score, state, dil)
|
| 248 |
+
if detoxify_called:
|
| 249 |
+
detoxify_call_count += 1
|
| 250 |
+
_log_pipeline_counts(early_exit_count, detoxify_call_count)
|
| 251 |
+
return result
|
| 252 |
|
| 253 |
|
| 254 |
def run_moderation_batch(texts, platform_dil: str = "tr", batch_size: int = 8):
|
| 255 |
state = _ensure_runtime_ready()
|
| 256 |
dil = "en" if platform_dil == "en" else "tr"
|
| 257 |
+
early_exit_count = 0
|
| 258 |
+
detoxify_call_count = 0
|
| 259 |
|
| 260 |
results = [None] * len(texts)
|
| 261 |
tr_pending = []
|
|
|
|
| 265 |
temiz = clean_text_nfkc(text)
|
| 266 |
|
| 267 |
if is_spam(temiz, dil):
|
| 268 |
+
early_exit_count += 1
|
| 269 |
results[idx] = (
|
| 270 |
"🗑️ SPAM/GİBBERİSH",
|
| 271 |
"Anlamsız veya tekrarlı içerik.",
|
|
|
|
| 278 |
|
| 279 |
profanity_hits, insult_hits = _extract_blacklist_hits(temiz, dil)
|
| 280 |
if profanity_hits or insult_hits:
|
| 281 |
+
early_exit_count += 1
|
| 282 |
results[idx] = _blacklist_early_result(profanity_hits, insult_hits, dil, temiz)
|
| 283 |
continue
|
| 284 |
|
| 285 |
if dil == "en":
|
| 286 |
+
gb = None
|
| 287 |
+
if state["GB_PIPE"] is not None:
|
| 288 |
+
gb_raw = state["GB_PIPE"](temiz)[0]
|
| 289 |
+
gb = {
|
| 290 |
+
"label": str(gb_raw.get("label", "")),
|
| 291 |
+
"score": float(gb_raw.get("score", 0.0)),
|
| 292 |
+
}
|
| 293 |
+
if gb.get("label", "").lower() == "noise" and gb.get("score", 0.0) > 0.98:
|
| 294 |
+
early_exit_count += 1
|
| 295 |
+
results[idx] = (
|
| 296 |
+
"🗑️ GIBBERISH/SPAM",
|
| 297 |
+
"Metin anlamsız karakter dizileri içeriyor.",
|
| 298 |
+
"LOW",
|
| 299 |
+
"en",
|
| 300 |
+
temiz,
|
| 301 |
+
{"gibberish": gb, "action": "MONITOR", "detox": {}},
|
| 302 |
+
)
|
| 303 |
+
continue
|
| 304 |
+
|
| 305 |
+
raw_res = state["D_EN"].predict(temiz)
|
| 306 |
+
detoxify_call_count += 1
|
| 307 |
+
res = {k: float(v) for k, v in raw_res.items()}
|
| 308 |
+
tox_score = float(res.get("toxicity", 0.0))
|
| 309 |
+
ins_score = float(res.get("insult", 0.0))
|
| 310 |
+
identity_attack = float(res.get("identity_attack", 0.0))
|
| 311 |
+
detail = {
|
| 312 |
+
"detox": res,
|
| 313 |
+
"insult": ins_score,
|
| 314 |
+
"toxicity": tox_score,
|
| 315 |
+
"identity_attack": identity_attack,
|
| 316 |
+
"hits": profanity_hits,
|
| 317 |
+
"insult_hits": insult_hits,
|
| 318 |
+
"gibberish": gb if state["GB_PIPE"] is not None else None,
|
| 319 |
+
}
|
| 320 |
+
verdict = calculate_verdict(
|
| 321 |
+
profanity_hits,
|
| 322 |
+
insult_hits,
|
| 323 |
+
{
|
| 324 |
+
"off_score": 0.0,
|
| 325 |
+
"detox_toxicity": tox_score,
|
| 326 |
+
},
|
| 327 |
+
)
|
| 328 |
+
detail.update({"action": ACTION_MAP.get(verdict["risk_level"], "MONITOR")})
|
| 329 |
+
results[idx] = (verdict["decision"], verdict["reason"], verdict["risk_level"], dil, temiz, detail)
|
| 330 |
continue
|
| 331 |
|
| 332 |
tr_pending.append((idx, temiz, profanity_hits, insult_hits))
|
|
|
|
| 336 |
off_scores = _tr_off_scores_batched(tr_pending_texts, state, batch_size=batch_size)
|
| 337 |
for pending, off_score in zip(tr_pending, off_scores):
|
| 338 |
idx, temiz, profanity_hits, insult_hits = pending
|
| 339 |
+
result, detoxify_called = _tr_result_with_off_score(temiz, profanity_hits, insult_hits, off_score, state, dil)
|
| 340 |
+
if detoxify_called:
|
| 341 |
+
detoxify_call_count += 1
|
| 342 |
+
results[idx] = result
|
| 343 |
+
|
| 344 |
+
_log_pipeline_counts(early_exit_count, detoxify_call_count)
|
| 345 |
|
| 346 |
return results
|