more fixes web + mobile

This commit is contained in:
Thomas Demirdjian
2025-11-20 11:26:24 +01:00
parent fe9b2b9e8d
commit 70c94c97b4
32 changed files with 1214 additions and 226 deletions
+76
View File
@@ -62,6 +62,45 @@ exports.analyseLyrics = async ({ title = "", lyrics }) => {
);
}
// Détection heuristique d'insultes explicites pour durcir la modération avant l'IA
const sanitizedLyrics = lyricsText
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/[^a-z0-9\s]/g, " ")
.replace(/\s+/g, " ")
.trim();
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const vulgarTerms = [
"trou du cul",
"trouduc",
"encule",
"enculer",
"connard",
"connasse",
"con",
"fdp",
"fils de pute",
"pute",
"putain",
"salope",
"salaud",
"enfoire",
"batard",
"ordure",
"ta gueule",
"nique ta mere",
"ntm",
];
const detectedVulgarities = vulgarTerms.filter((term) => {
const pattern = new RegExp(`\\b${escapeRegex(term)}\\b`, "i");
return pattern.test(sanitizedLyrics);
});
const vulgarityHints = detectedVulgarities.length
? `<VULGARITES_DETECTEES>${detectedVulgarities.join(", ")}</VULGARITES_DETECTEES>`
: "<VULGARITES_DETECTEES>Aucune détectée heuristiquement.</VULGARITES_DETECTEES>";
// --- Schéma de sortie (inchangé pour compatibilité) ---
const moderationSchema = z.object({
title: z.string().describe("Titre analysé (copie du titre fourni)"),
@@ -101,6 +140,7 @@ Objectif: protéger les utilisateurs en BLOQUANT uniquement les cas réellement
Sois STRICT sur les risques graves, mais NE SOIS PAS PUNITIF envers l'expression artistique.
Évalue TOUJOURS: 1) l'intention, 2) le contexte (récit, citation, dénonciation, rôle/persona), 3) la cible, 4) la probabilité de tort réel.
Ne laisse PAS passer des insultes vulgaires explicites (connard, trou du cul, enculé, fdp, salope, etc.): au minimum flagged=true avec un score significatif; blocked=true si c'est dirigé ou répété sans distance.
Retourne UNIQUEMENT un JSON conforme au schéma. Pas de texte hors JSON.
`.trim();
@@ -135,6 +175,12 @@ Retourne UNIQUEMENT un JSON conforme au schéma. Pas de texte hors JSON.
<CAS>Si le passage relève de la narration, satire, critique ou roleplay: privilégie flagged=true et blocked=false.</CAS>
<CAS>Si le passage constitue une injonction réelle ou un appel clair au tort: blocked=true.</CAS>
</SECTION>
<SECTION id="E">
<TITRE>INSULTES ET VULGARITES</TITRE>
<CAS>Insultes vulgaires explicites (trou du cul, connard, enculé, fdp, salope, ta gueule, nique ta mere, etc.) = flagged=true minimum et score ≥ 0.45.</CAS>
<CAS>Si ces insultes visent quelqu'un de manière directe ou répétée, ou incitent à rabaisser/attaquer: blocked=true ou severity high/critical (au moins high si pas certain).</CAS>
<CAS>Si c'est de l'auto-dérision ou du langage cru sans cible ni incitation: flagged=true mais blocked=false, score limité.</CAS>
</SECTION>
</POLITIQUE>
`.trim();
@@ -153,9 +199,15 @@ ${lyricsText}
<REGLE>"blocked" = true UNIQUEMENT pour les cas graves listés dans la section A.</REGLE>
<REGLE>Si le contenu relève du récit, de la critique, de la satire ou d'une mise en contexte artistique, laisse blocked=false (flagged=true si nécessaire).</REGLE>
<REGLE>Fournis des "excerpts" courts en citant exactement les passages sensibles.</REGLE>
<REGLE>Si des insultes vulgaires explicites sont présentes, flagged=true au minimum (score &gt;=0.45) et blocked=true si elles visent clairement quelqu'un.</REGLE>
</DIRECTIVES>
${decisionPolicy}
<INDICES_SUPPLEMENTAIRES>
${vulgarityHints}
<REGLE_VULGARITES>Si la liste ci-dessus n'est pas vide, considère ces termes comme signaux forts de harcèlement verbal: flagged=true au minimum et score ajusté en conséquence.</REGLE_VULGARITES>
</INDICES_SUPPLEMENTAIRES>
`.trim();
const { output } = await genkit({
@@ -167,6 +219,30 @@ ${decisionPolicy}
output: { schema: moderationSchema },
});
if (output && detectedVulgarities.length) {
output.flagged = true;
const bumpScore = detectedVulgarities.length >= 3 ? 0.65 : 0.5;
const baseScore = Number.isFinite(output.score) ? output.score : 0;
output.score = Math.min(1, Math.max(baseScore, bumpScore));
const reasons = Array.isArray(output.reasons) ? output.reasons : [];
const reason = `Vulgarités détectées (${detectedVulgarities.join(", ")})`;
if (!reasons.includes(reason)) reasons.push(reason);
output.reasons = reasons;
const excerpts = Array.isArray(output.excerpts) ? output.excerpts : [];
const slots = Math.max(0, 10 - excerpts.length);
if (slots > 0) {
const severity = detectedVulgarities.length > 2 ? "high" : "medium";
const newExcerpts = detectedVulgarities.slice(0, slots).map((term) => ({
quote: term,
category: "harassment",
severity,
}));
output.excerpts = [...excerpts, ...newExcerpts];
}
}
return output;
};