Spaces:
Paused
Paused
Z User
v5.8.9: Fix dedup, upgrade Qwen3.6-35B-A3B, humanize v2, personality v2, /messages endpoint
b4c36fb | /** | |
| * ============================================ | |
| * π humanizer.js β Suavizador de Respuestas v2.0 | |
| * ============================================ | |
| * Hace que Zelin suene como alguien REAL en Discord. | |
| * Inspirado en Character.AI, Nomi.ai, SillyTavern: | |
| * - Elimina patrones robΓ³ticos agresivamente | |
| * - AΓ±ade imperfecciones naturales (sin corromper) | |
| * - Transforma respuestas de IA en chat humano | |
| */ | |
| // Conectores formales β informales | |
| const CONNECTORS = { | |
| 'por lo tanto' : 'asΓ que', | |
| 'sin embargo' : 'pero', | |
| 'no obstante' : 'aunque', | |
| 'asimismo' : 'tambiΓ©n', | |
| 'por consiguiente' : 'entonces', | |
| 'en conclusiΓ³n' : 'en fin', | |
| 'en resumen' : 'bΓ‘sicamente', | |
| 'en definitiva' : 'al final', | |
| 'por otra parte' : 'tambiΓ©n', | |
| 'cabe destacar' : 'hay que decir que', | |
| 'cabe mencionar' : 'hay que mencionar que', | |
| 'es importante destacar': 'lo importante es que', | |
| 'resulta que' : 'pues', | |
| 'a continuaciΓ³n' : '', | |
| 'procederΓ© a' : 'voy a', | |
| 'me complace' : '', | |
| 'con mucho gusto' : '', | |
| }; | |
| // Frases robΓ³ticas β humanas (EXPANDIDO - basado en Character.AI anti-patterns) | |
| const ROBOTIC = { | |
| 'Β‘Por supuesto!' : 'claro', | |
| 'Β‘Claro que sΓ!' : 'sip', | |
| 'Β‘Encantado/a!' : '', | |
| 'Β‘Encantada!' : '', | |
| 'Β‘Encantado!' : '', | |
| 'Como IA que soy,' : '', | |
| 'Como IA,' : '', | |
| 'Como asistente,' : '', | |
| 'Como modelo de lenguaje,' : '', | |
| 'Como modelo de IA,' : '', | |
| 'No dudes en preguntar': 'cualquier cosa me dices', | |
| 'Estoy aquΓ para ayudarte': '', | |
| 'Estoy aquΓ para ayudarte!' : '', | |
| 'Β‘Espero haberte ayudado!' : '', | |
| 'Si tienes mΓ‘s preguntas' : 'si necesitas mΓ‘s', | |
| 'Con gusto te ayudo' : '', | |
| 'Es un placer' : '', | |
| 'Β‘Claro!' : 'claro', | |
| 'Entendido,' : '', | |
| 'Por supuesto,' : '', | |
| 'Perfecto,' : '', | |
| 'Β‘Perfecto!' : '', | |
| 'A continuaciΓ³n,' : '', | |
| 'Dicho esto,' : '', | |
| 'En ese caso,' : 'entonces', | |
| 'Desde luego' : 'claro', | |
| 'Sin duda,' : '', | |
| 'Efectivamente,' : '', | |
| 'Con gusto,' : '', | |
| 'PermΓteme ' : 'dΓ©jame ', | |
| 'Por supuesto que' : '', | |
| 'Me alegra que preguntes' : '', | |
| 'Gran pregunta' : '', | |
| 'Excelente pregunta' : '', | |
| 'Interesante pregunta' : '', | |
| 'Β‘Entendido!' : 'entendido', | |
| 'Como mencionΓ© anteriormente': 'como dije', | |
| 'En resumen,' : '', | |
| 'Para concluir,' : '', | |
| 'En conclusiΓ³n,' : '', | |
| 'Espero que esto te ayude' : '', | |
| 'Espero haber aclarado' : '', | |
| 'No te preocupes,' : '', | |
| 'Te entiendo,' : '', | |
| 'DΓ©jame ayudarte' : '', | |
| 'Estoy a tu disposiciΓ³n' : '', | |
| 'ΒΏEn quΓ© mΓ‘s puedo ayudarte?' : '', | |
| 'ΒΏPuedo ayudarte en algo mΓ‘s?' : '', | |
| 'Β‘Estoy aquΓ para lo que necesites!' : '', | |
| 'No tengo opiniones personales' : '', | |
| 'Como modelo' : '', | |
| 'No puedo tener opiniones' : '', | |
| 'Es importante tener en cuenta que' : '', | |
| 'Vale la pena mencionar que' : '', | |
| 'Debo seΓ±alar que' : '', | |
| 'Es relevante mencionar' : '', | |
| }; | |
| // Γnfasis exagerado β natural | |
| const EMPHASIS = { | |
| 'extremadamente' : 'muy', | |
| 'absolutamente' : 'bastante', | |
| 'totalmente' : 'bastante', | |
| 'completamente' : 'bastante', | |
| 'perfectamente' : 'bien', | |
| 'extraordinario' : 'bastante bueno', | |
| 'imprescindible' : 'necesario', | |
| 'fundamental' : 'importante', | |
| 'crucial' : 'clave', | |
| 'increΓblemente' : 'muy', | |
| 'verdaderamente' : 'muy', | |
| 'sumamente' : 'muy', | |
| 'notablemente' : 'bastante', | |
| }; | |
| // PATRONES DE APERTURA ROBΓTICA - eliminarlos con regex | |
| const ROBOTIC_PREFIXES = [ | |
| /^(bien,\s*)/i, | |
| /^(ok,\s*)/i, | |
| /^(vale,\s*)/i, | |
| /^(claro,\s*(que|sΓ|te|eso|este|la|el|lo|me|le|se)\s+)/i, | |
| /^(sΓ,\s*(claro|exacto|obvio|por supuesto|seguro)\s*[,.]?\s*)/i, | |
| /^(exacto[,.]?\s*)/i, | |
| /^(asΓ es[,.]?\s*)/i, | |
| /^(eso es correcto[,.]?\s*)/i, | |
| /^(correcto[,.]?\s*)/i, | |
| ]; | |
| /** | |
| * Humaniza el texto de la IA. | |
| * Hace que Zelin suene como alguien real escribiendo en Discord β | |
| * casual, con errores naturales, sin ortografΓa perfecta de robot. | |
| */ | |
| export function humanize(text) { | |
| if (!text || typeof text !== 'string') return text; | |
| let t = text.trim(); | |
| // No tocar JSONs ni respuestas tΓ©cnicas | |
| if (t.startsWith('{') || t.startsWith('[')) return t; | |
| if (t.length < 3) return t; | |
| // ββ ELIMINAR ARTEFACTOS DE PENSAMIENTO ββββββββββββββββββββββββββββββββββββββ | |
| t = t.replace(/<think[\s\S]*?<\/think>/gi, '').trim(); | |
| t = t.replace(/<thinking[\s\S]*?<\/thinking>/gi, '').trim(); | |
| t = t.replace(/^Pensamiento:\s*/gim, '').trim(); | |
| t = t.replace(/^Pienso[\s\S]*?\nRespuesta:\s*/i, ''); | |
| // Remove numbered reasoning steps | |
| const stepPattern = /^(\d+\.\s+.*\n?)+/gm; | |
| if (stepPattern.test(t) && t.split('\n').filter(l => /^\d+\.\s/.test(l.trim())).length >= 2) { | |
| const parts = t.split('\n\n'); | |
| const nonStepParts = parts.filter(p => !/^\d+\.\s/m.test(p.trim())); | |
| if (nonStepParts.length > 0) { | |
| t = nonStepParts.join('\n\n').trim(); | |
| } else { | |
| const lines = t.split('\n').filter(l => l.trim()); | |
| t = lines.length > 1 ? lines[lines.length - 1].replace(/^\d+\.\s*/, '').trim() : t; | |
| } | |
| } | |
| // ββ LIMPIAR FORMATO MARKDOWN ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| t = t.replace(/\*\*([^*<>]{1,120})\*\*/g, '$1'); | |
| t = t.replace(/\*[^*]{1,40}\*/g, '').trim(); | |
| t = t.replace(/\b_[^_]{1,40}_\b/g, '').trim(); | |
| t = t.replace(/^#{1,3}\s+/gm, ''); | |
| t = t.replace(/^---+\s*$/gm, '').trim(); | |
| t = t.replace(/^===+\s*$/gm, '').trim(); | |
| // ββ REEMPLAZOS LΓXICOS ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| for (const [formal, informal] of Object.entries(CONNECTORS)) { | |
| t = t.replace(new RegExp(`\\b${escapeRegex(formal)}\\b`, 'gi'), informal); | |
| } | |
| for (const [robotic, human] of Object.entries(ROBOTIC)) { | |
| t = t.replace(new RegExp(escapeRegex(robotic), 'gi'), human); | |
| } | |
| for (const [exag, natural] of Object.entries(EMPHASIS)) { | |
| t = t.replace(new RegExp(`\\b${escapeRegex(exag)}\\b`, 'gi'), natural); | |
| } | |
| // ββ ELIMINAR PREFIJOS ROBΓTICOS βββββββββββββββββββββββββββββββββββββββββββββ | |
| for (const prefix of ROBOTIC_PREFIXES) { | |
| t = t.replace(prefix, ''); | |
| } | |
| // ββ FIX PUNTUACIΓN ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| t = t.replace(/ΒΏ([^?]+)\?\?+/g, 'ΒΏ$1?'); | |
| t = t.replace(/!{2,}/g, '!'); | |
| t = t.replace(/\?{2,}/g, '?'); | |
| t = t.replace(/,\s*,/g, ','); | |
| t = t.replace(/\.{4,}/g, '...'); | |
| t = t.replace(/,([^\s])/g, ', $1'); | |
| t = t.replace(/^ΒΏ([^?]+[.!])$/gm, '$1'); | |
| // ββ NATURALIZACIΓN DE ESCRITURA ββββββββββββββββββββββββββββββββββββββββββββ | |
| const isConversational = t.length < 400 && !t.includes('```') && !t.includes('http'); | |
| if (isConversational) { | |
| // 1. Quitar punto final en mensajes cortos (nadie pone punto en Discord) | |
| if (!t.includes('\n') && t.endsWith('.') && t.length < 150) { | |
| // No quitar puntos en abreviaturas o URLs | |
| const lastWord = t.slice(0, -1).split(' ').pop(); | |
| if (lastWord && lastWord.length > 2 && !lastWord.includes('/')) { | |
| t = t.slice(0, -1); | |
| } | |
| } | |
| // 2. MinΓΊsculas al inicio en frases cortas (estilo chat real) | |
| if (t.length < 80 && !t.startsWith('ΒΏ') && !t.startsWith('Β‘') && !/^[A-Z]{2,}/.test(t)) { | |
| t = t.charAt(0).toLowerCase() + t.slice(1); | |
| } | |
| // 3. Sustituciones de palabras β mΓ‘s casual (como habla la gente en Discord) | |
| const CASUAL = { | |
| 'tambiΓ©n': Math.random() > 0.35 ? 'tambiΓ©n' : 'tmb', | |
| 'porque': Math.random() > 0.45 ? 'porque' : 'pq', | |
| 'quΓ©': Math.random() > 0.6 ? 'quΓ©' : 'q', | |
| 'estΓ‘s': Math.random() > 0.5 ? 'estΓ‘s' : 'tΓ‘s', | |
| 'que': Math.random() > 0.75 ? 'que' : 'q', | |
| 'no sΓ©': Math.random() > 0.4 ? 'no sΓ©' : 'ni idea', | |
| 'sΓ': Math.random() > 0.6 ? 'sΓ' : 'sip', | |
| 'no': Math.random() > 0.7 ? 'no' : 'nop', | |
| 'verdad': Math.random() > 0.6 ? 'verdad' : 'vrd', | |
| 'hermano': Math.random() > 0.5 ? 'hermano' : 'bro', | |
| 'amigo': Math.random() > 0.5 ? 'amigo' : 'bro', | |
| 'para': Math.random() > 0.8 ? 'para' : 'pa', | |
| }; | |
| let subs = 0; | |
| for (const [formal, casual] of Object.entries(CASUAL)) { | |
| if (subs >= 2) break; | |
| if (casual !== formal && Math.random() > 0.5) { | |
| const regex = new RegExp(`\\b${escapeRegex(formal)}\\b`, 'g'); | |
| const newT = t.replace(regex, casual); | |
| if (newT !== t) { t = newT; subs++; } | |
| } | |
| } | |
| // 4. A veces "jaja" β "jajaja" o "jajaja" β "jaja" (naturalidad) | |
| if (Math.random() > 0.6) { | |
| t = t.replace(/\bjajaja+\b/gi, m => Math.random() > 0.5 ? 'jaja' : 'jajaja'); | |
| } | |
| // 5. A veces "xd" β "xdd" o "XDD" (como escriben los gamers) | |
| if (/\bxd\b/i.test(t) && Math.random() > 0.4) { | |
| t = t.replace(/\bxd\b/gi, m => Math.random() > 0.5 ? 'xdd' : 'xd'); | |
| } | |
| } | |
| // ββ ANTI-RESPUESTA-LARGA: truncar divagaciΓ³n ββββββββββββββββββββββββββββββββ | |
| // Si la respuesta es mΓ‘s de 3 lΓneas para algo conversacional, probablemente divaga | |
| if (isConversational && t.length > 300) { | |
| const lines = t.split('\n').filter(l => l.trim()); | |
| if (lines.length > 3) { | |
| // Tomar solo las primeras 2-3 lΓneas significativas | |
| const shortLines = lines.slice(0, Math.min(3, lines.length)); | |
| const lastLine = shortLines[shortLines.length - 1]; | |
| // Tratar de cortar en la ΓΊltima puntuaciΓ³n | |
| const lastPunct = Math.max( | |
| lastLine.lastIndexOf('.'), | |
| lastLine.lastIndexOf('!'), | |
| lastLine.lastIndexOf('?'), | |
| ); | |
| if (lastPunct > 20) { | |
| shortLines[shortLines.length - 1] = lastLine.slice(0, lastPunct + 1); | |
| } | |
| t = shortLines.join('\n'); | |
| } | |
| } | |
| // Limpiar espacios y lΓneas vacΓas extra | |
| t = t.replace(/\n{3,}/g, '\n\n').replace(/ +/g, ' ').trim(); | |
| // Nunca devolver cadena vacΓa | |
| return t || text.trim(); | |
| } | |
| function escapeRegex(s) { | |
| return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | |
| } | |