{{hTitle}} {{aMod.tag}}

Budget diario
${{lm.today?.total_spent_usd?.toFixed(4)||'0.0000'}} / ${{(+lm.limits?.daily_budget_usd||0).toFixed(2)}}
Queda hoy
${{lm.today?.budget_remaining_usd?.toFixed(4)||'0.0000'}}
{{lm.today?.budget_used_pct?.toFixed(1)||'0'}}% usado · {{lm.today?.total_chats||0}} chats · {{lm.today?.unique_ips||0}} IPs únicas Límite por IP: ${{(+lm.limits?.per_ip_daily_usd||0).toFixed(2)}}/día
⚠️ Límites de gasto desactivados. Acceso libre. Podés gastar sin tope.
💰 Costo Total
${{as.totalCostUsd?.toFixed(4)||'0.0000'}}
💰 Costo Hoy
${{as.todayCostUsd?.toFixed(4)||'0.0000'}}
💬 Chats
{{as.totalChats||0}}
💵 Prom/Chat
${{as.avgCostPerChat?.toFixed(6)||'0'}}
📥 Tokens In
{{(as.totalTokensIn||0).toLocaleString()}}
📤 Tokens Out
{{(as.totalTokensOut||0).toLocaleString()}}
🔑 Tokens Activos
{{as.activeTokens||0}}
📊 Uso Hoy
{{as.todayUsage||0}}

🌐 Top IPs hoy

IPChatsAccionesGasto
{{row.ip_address}} {{row.chats}} {{row.actions}} ${{(+row.cost||0).toFixed(4)}}

🤖 Gasto por proveedor

ProveedorTipoChatsTokensGasto
{{p.name}} {{p.type}} {{p.uses}} {{(+p.t_in).toLocaleString()}} / {{(+p.t_out).toLocaleString()}} ${{(+p.cost||0).toFixed(4)}}

💬 Últimas consultas

{{l.module||'—'}} {{l.ip_address||'—'}} {{l.provider_name||l.model||'—'}}
${{(+l.cost_usd).toFixed(4)}} {{l.created_at}}
💬 {{l.prompt_preview}}
📭

Sin actividad

{{usage.length}} / {{usageTotal}}
Calidad:
⚠️ {{usageQualityAlert}} preguntas mal respondidas en las últimas 24 horas — vale la pena revisarlas y sumar info
{{scoreEmoji(+l.quality_score)}} {{l.quality_score}} {{l.action}} {{l.module}} {{l.ip_address||'—'}} · {{l.provider_name}} · {{l.model}}
📥 {{(+l.tokens_in).toLocaleString()}} 📤 {{(+l.tokens_out).toLocaleString()}} ${{(+l.cost_usd).toFixed(4)}} {{l.created_at}}
P: {{l.prompt_preview}}
R: {{l.response_preview}}
{{r.icon}} {{r.text}}
📭

Sin actividad en el rango {{usageFilters.quality==='bad'?'(con score ≤4)':''}}

Controlá el gasto diario en API de IA. Cuando se llega al tope, el sistema responde 429 y los usuarios esperan al día siguiente.

Activar límites de gasto
Sin esto, el gasto corre libre. Te lo recomiendo activado si liberás el acceso.
⚠️ Los límites no se aplicarán hasta que prendas el toggle de arriba.
Cuando la suma de todos los gastos del día llega a este valor, nadie puede chatear más hasta mañana.
Cada IP individual tiene este tope. Bloquea abusadores sin cortarle el servicio a los demás. Poné 0 para desactivar este límite.

📊 Estado actual

Gastado hoy
${{lm.today?.total_spent_usd?.toFixed(4)||'0.0000'}}
Chats hoy
{{lm.today?.total_chats||0}}
IPs únicas hoy
{{lm.today?.unique_ips||0}}
% Usado
{{lm.today?.budget_used_pct?.toFixed(1)||'0'}}%
💡 Sugerencias de budget
Testing: global $1-$2/día, por IP $0.20/día
Early access: global $5-$10/día, por IP $0.50/día
Beta cerrada: global $20-$50/día, por IP $1-$2/día

Gasto aproximado de un chat típico:
• Gemini 2.0 Flash: ~$0.0002 → 5000 chats por $1
• DeepSeek Chat: ~$0.0006 → 1700 chats por $1
• Claude Haiku: ~$0.003 → 330 chats por $1
• Claude Sonnet: ~$0.015 → 65 chats por $1
TokenEtiquetaSecciónUsosEstado
{{t.token.substring(0,16)}}... {{t.label}}{{t.section}}{{t.usage_count}} {{t.is_active?'On':'Off'}}
🔑

Sin tokens

📚 Wiki Knowledge Base

El archivo wiki_kb.json que usa la IA para responder consultas de MSPro IA.

⏳ Cargando...

Instrucciones obligatorias para la IA.

📌
{{r.rule_value}}
{{r.section}}
⚙️

Sin reglas. La IA opera libre.

💡 Ejemplos

• "Level mínimo: 300"
• "Zen máximo: 5.000.000"
• "MR requiere mínimo 200 resets"
• "% clase: entre 50% y 200%"

Agrupa preguntas similares de usuarios para crear respuestas canónicas y convertirlas en docs.

Cierra el loop: las preguntas nuevas alimentan los docs, y los docs mejoran las respuestas futuras.

📝 Preguntas totales
{{faqStats.total_questions||0}}
📦 Sin agrupar
{{faqStats.unclustered||0}}
🗂️ Clusters
{{faqStats.total_clusters||0}}
✅ Resueltos
{{faqStats.resolved_clusters||0}}
📅 Hoy
{{faqStats.today_questions||0}}
📅 7 días
{{faqStats.week_questions||0}}
⏳ Pendientes
{{faqStats.pending_clusters||0}}
💬 Respondidos
{{(+faqStats.total_clusters||0)-(+faqStats.pending_clusters||0)-(+faqStats.resolved_clusters||0)}}
🗂️ Clusters ({{faqClusters.length}})
📦 Sin agrupar
📝 Todas las preguntas
🔗 {{faqSelected.length}} clusters seleccionados Elegí cuál queda como primario (se preserva su label y respuesta). Los otros se fusionan.
🗂️

Sin clusters todavía. Cargá preguntas de usuarios y corré ⚡ Re-agrupar.

{{c.status}} {{c.module||'global'}} {{c.question_count}} preguntas 📄 doc #{{c.doc_id}} ⭐ PRIMARIO

{{c.label}}

{{c.canonical_answer}}
Preguntas en este cluster ({{c.questions?.length||0}})
{{q.question}}
{{q.ip_address}} · {{q.created_at}}
📝

Sin preguntas.

{{q.module||'—'}} Cluster #{{q.cluster_id}} Sin cluster {{q.ip_address}}
${{(+q.cost_usd).toFixed(4)}} {{q.created_at}}
💬 {{q.question}}
Ver respuesta
{{q.answer}}

Radiografía de lo que cada módulo le "enseña" a la IA antes de chatear.

Sumá docs personalizados que se inyectan al system prompt cuando se consulta ese módulo.

⏳ Analizando...
{{mod.icon}}

{{mod.name}}

id: {{mod.id}}
~{{mod.total_tokens_est.toLocaleString()}}
tokens estimados
{{mod.total_chars.toLocaleString()}} chars
📦 Código base ⚙️ Reglas IA 📄 Docs custom 📚 Wiki KB
{{src.label}}
{{(+src.chars).toLocaleString()}} chars · {{src.count}} {{src.count===1?'item':'items'}} · {{src.wiki_stats.articles}} artículos
{{mod.preview}}
… ({{(mod.total_chars-3000).toLocaleString()}} chars más)

📄 Docs personalizados{{knowledge.filter?' · '+knowledge.filter:''}}

📄

Sin docs. Agregá el primero con "+ Agregar Doc".

{{d.title}} {{d.section}} INACTIVO
~{{(+d.tokens_est||0).toLocaleString()}} tokens · fuente: {{d.source}} · actualizado: {{d.updated_at}}
💡 Cómo funciona
Los docs personalizados se concatenan al system prompt del módulo correspondiente. Ej: un doc con section=reset-system se manda sólo cuando alguien chatea en ese módulo. Con section=global se manda en todos.

Tip: menos es más. Si sumás 5000 tokens de docs, cada respuesta va a ser más lenta y más cara. Cortá en docs específicos (ej: "FAQ clientes MSPro Season 17", "Errores comunes al instalar", "Política de reset rates") y activá sólo los que aplican.

Listado limpio de preguntas que recibió la IA, agrupadas por similitud semántica. Si la misma pregunta se hizo varias veces (con palabras diferentes), aparece una sola vez con su contador.

Preguntas únicas
{{questionsStats.total_clusters||0}}
Total preguntas
{{questionsStats.total_questions||0}}
Problemáticas
{{questionsStats.bad||0}}
Sin score
{{questionsStats.unscored||0}}
Hoy
{{questionsStats.today||0}}
📋

No hay preguntas con esos filtros.

{{scoreEmoji(c.avg_quality)}} {{Number(c.avg_quality).toFixed(1)}}/10 ⚪ s/score {{c.module||'?'}} 🔁 {{c.question_count}}× {{c.last_asked_at?relTime(c.last_asked_at):''}}
{{c.label||c.sample_question}}
{{qFilters.offset+1}}-{{Math.min(qFilters.offset+qFilters.limit,qTotal)}} de {{qTotal}}

Indexá los archivos .txt/.ini/.lua del server y del encoder del cliente para que la IA los conozca.

Podés subir archivos individuales o ZIP. Después agrupás los relacionados (ej: Monster.txt server + Monsters.txt encoder).

📄 Archivos
{{sfStats.total_files||0}}
🖥️ Server
{{sfStats.server_files||0}}
🎨 Encoder
{{sfStats.client_files||0}}
🔗 Grupos
{{sfStats.total_groups||0}}
⚠️ Tenés {{sfStats.pending_suggestions}} sugerencias de relación pendientes. Revisalas en la pestaña "Sugerencias".
📄 Archivos ({{sfFiles.length}})
🔗 Grupos ({{sfGroups.length}})
💡 Sugerencias ({{sfStats.pending_suggestions}})
📂

Sin archivos todavía. Subí el primer ZIP.

{{f.side==='server'?'SRV':'ENC'}} {{f.path}} 🔗{{f.group_count}} {{f.system}} {{fmtBytes(f.size_bytes)}}
🌲

Sin archivos todavía.

🔗

Sin grupos. Armá el primero con archivos que sepas que están relacionados.

{{g.name}}

{{g.description}}
{{g.member_count}} archivos · actualizado: {{g.updated_at}}
{{m.side==='server'?'SRV':'ENC'}} {{m.path}}
💡

Sin sugerencias pendientes. Se generan automáticamente cuando subís archivos que mencionan a otros (ej: "lado servidor Monster.txt").

{{s.reason}}
{{s.source_side==='server'?'SRV':'ENC'}} {{s.source_path}}
↓ se relaciona con ↓
{{s.suggested_side==='server'?'SRV':'ENC'}} {{s.suggested_path}}
⚠️ Archivo referenciado {{s.suggested_name}} no está indexado todavía. Subilo para poder crear la relación.

Correcciones que vos curaste para enseñarle al sistema. Cada corrección es un ejemplo de pregunta + archivos correctos + respuesta esperada que se inyecta en preguntas similares.

El sistema busca correcciones similares (TF-IDF) en cada pregunta y las usa como ejemplos few-shot.

Activas
{{correctionsStats.total_active}}
Aplicadas
{{correctionsStats.total_applied||0}}
Tags únicos
{{(correctionsStats.top_tags||[]).length}}
Archivos refs
{{(correctionsStats.top_files||[]).length}}
📝

Sin correcciones todavía.
Andá a Actividad, abrí una pregunta mal respondida, y clickeá "📝 Corregir esta respuesta".

{{c.module}} ✓ {{c.times_applied}} usos
{{c.created_at}}
{{c.question}}
{{(c.correct_answer||'').substring(0,200)}}...
{{f}}

Probá una pregunta y ves exactamente lo que la IA recibe como contexto: grupos detectados, sus notas, archivos, correcciones aplicables y el bloque que se inyecta al system prompt.

Útil para diagnosticar por qué la IA no menciona X archivo o Y nota que vos curaste.

{{debugResult.has_results?'✅ Encontró contexto':'⚠️ Sin resultados'}} Tokens: {{(debugResult.tokens||[]).join(', ')}} Grupos: {{(debugResult.groups_found||[]).length}} Archivos: {{(debugResult.files_found||[]).length}} Correcciones: {{debugResult.corrections_found||0}} ❌ Tabla notes NO existe 📌 Total notas en DB: {{debugResult.total_notes_in_db}}
🔬 Diagnóstico: notas por group_id en DB
group_id={{gid}} → {{cnt}} nota(s)
🧪 Query directo (con group_id casteado a int)
group_id={{gid}} (tipo: {{check.group_id_type}}) → {{check.count_returned}} nota(s) traídas ERROR: {{check.error}}

📦 Grupos detectados

{{g.name}} (score {{g.score?.toFixed?.(2)||g.score}})
{{g.description}}
Miembros ({{g.members_count}}):
{{m}}
📌 Notas curadas ({{g.notes_count}}):
{{n.path}} ({{n.ref_type}})
{{n.note}}
Sin notas adicionales

📄 Archivos detectados

{{f.side==='server'?'SRV':'ENC'}} {{f.path}} score {{f.score?.toFixed?.(2)||f.score}}

📝 Correcciones aplicables

{{c.level}} ({{c.score}}) {{c.question}}
📋 Ver bloque completo que se inyecta al system prompt ({{debugResult.system_prompt_block_length||0}} chars)
{{debugResult.system_prompt_block||'(vacío)'}}
🐛

Escribí una pregunta arriba y clickeá "Probar" para ver qué contexto recibe la IA.

🛠️ Auditor de notas

Si el debug arriba muestra que las notas están en un group_id distinto al esperado, podés moverlas masivamente al grupo correcto.

Notas existentes ({{allNotesAudit.notes.length}}):
group_id={{n.group_id}} ({{n.group_name||'❌ grupo INEXISTENTE'}}) · {{n.path}}[sin path]
{{n.note.substring(0,120)}}...
🔧 Mover notas masivamente
Desde group_id: → hacia group_id:
No hay notas en la DB.

Sistema asistido por IA para optimizar las notas y observaciones de tus grupos. Sugiere tags automáticamente y detecta gaps de cobertura analizando preguntas problemáticas.

Costo aproximado: menos de $0.01 USD por uso completo (DeepSeek).

⚠️ {{aiaPendingCount}} cambio(s) sin guardar
📝

No hay notas cargadas todavía.
Cargá notas en los grupos primero.

📦 {{n.group_name||'(sin grupo)'}} {{n.path}} {{n.ref_type}}
{{n.note.substring(0,200)}}...

La IA va a leer las preguntas con score ≤ 4 de los últimos 30 días y proponer notas a crear que mejorarían las respuestas futuras.

No hay sugerencias.
Las preguntas problemáticas no requieren notas nuevas, o no hay preguntas problemáticas.

Analizadas {{auditResult.total_questions_analyzed}} pregunta(s), {{auditResult.total_suggestions}} sugerencia(s) de notas nuevas:
Pregunta #{{s.question_id}} · score {{s.question_score}}/10
"{{s.question_text}}"
🔍 {{s.diagnosis}}
📌 Nota sugerida
{{s.suggested_note.path}}
Tipo: {{s.suggested_note.ref_type}}
{{s.suggested_note.note}}
Tags: {{s.suggested_note.tags}}
→ Grupo destino: {{getGroupName(s.suggested_note.target_group_id)}}

Configurá a qué API de IA se conecta el sistema. Podés tener varios y marcar uno como default.

Las API keys se guardan cifradas con AES-256-CBC.

🤖

Sin proveedores. Agregá uno para empezar.

{{p.name}}

{{p.type}} DEFAULT {{+p.is_active?'Activo':'Inactivo'}} 🔑
{{p.api_url}}
Modelo: {{p.default_model}} · temp {{p.temperature}} · max {{p.max_tokens}}tk
{{p.notes}}
{{m.model_id}}${{(+m.cost_in_per_1m).toFixed(2)}}/M
{{providerTestResults[p.id].ok?'✅ Conectado':'❌ Falló'}}
{{providerTestResults[p.id].content}}
{{providerTestResults[p.id].error}}
💡 Dónde conseguir API keys:
Anthropic Claude: console.anthropic.com → API Keys
Google Gemini: aistudio.google.com → Get API Key (tiene tier gratis)
DeepSeek: platform.deepseek.com → API Keys (muy barato)
OpenAI: platform.openai.com → API Keys
Groq: console.groq.com (gratis con límites)
Ollama: sin key, corrés tu propio server

🔑 Nuevo Token

📌 Nueva Regla

{{toast.msg}}

{{pvForm.id?'✏️ Editar':'➕ Nuevo'}} Proveedor IA

{{m.label||m.id}}
Guardá el proveedor primero para poder listar modelos vía API.
📋 Modelos habilitados (con pricing)
Opcional: cargá pricing para calcular costo por chat (USD por 1M tokens). En 0 no se calcula.
Proveedor activo

⬆️ Subir archivos a {{multiUpload.basePath||'raíz'}}

Los archivos se van a agregar/reemplazar en {{multiUpload.basePath||'(raíz)'}} como {{multiUpload.side==='server'?'Server':'Encoder'}}.

📎
Elegí uno o varios archivos, o arrastralos acá
.txt · .ini · .cfg · .lua · .xml · .json · .md
{{multiUpload.basePath}}{{f.name}}
{{fmtBytes(f.size)}}

📦 Importar archivos del ZIP

Encontré {{zipInspect.total_files}} archivos de texto. {{zipInspect.skipped_binary}} binarios saltados.

ℹ️ Detectado carpeta raíz común: {{zipInspect.common_root}} — se va a quitar de los paths para que queden limpios.
Seleccionados: {{zipInspect.selected.length}}
{{f.path}}
🔄 existe {{fmtBytes(f.size)}}

Cargando...

{{sfDetail.file.filename}}

{{sfDetail.file.path}}
{{sfDetail.file.side==='server'?'SERVER':'ENCODER'}} {{sfDetail.file.system}} {{fmtBytes(sfDetail.file.size_bytes)}} · {{sfDetail.file.line_count}} líneas
📋 Headers
📄 Contenido
🔗 Grupos ({{sfDetail.file.groups?.length||0}})
📝 Notas
{{sfDetail.file.headers||'(sin headers detectados)'}}
{{sfDetail.file.content}}

Este archivo no está en ningún grupo todavía.

{{g.name}}
{{g.description}}
{{m.side==='server'?'SRV':'ENC'}} {{m.path}}
Sistema inferido: {{sfDetail.file.system}}. Cambialo si querés:
💡 Sugerencias de relación detectadas
{{s.reason}}
{{s.suggested_path}}
Archivo "{{s.suggested_name}}" no indexado.

{{groupForm.id?'✏️ Editar grupo #'+groupForm.id:'➕ Nuevo grupo'}}

🖥️ SERVER
⏳ Cargando...
{{grpSrv.q?'Sin resultados':'Escribí para buscar'}}
{{f.path}} {{f.system}}
🎨 ENCODER
⏳ Cargando...
{{grpEnc.q?'Sin resultados':'Escribí para buscar'}}
{{f.path}} {{f.system}}
⭐ Miembros del grupo ({{groupForm.member_ids.length}})
{{groupForm.members_meta[fid].side==='server'?'SRV':'ENC'}} {{(groupForm.members_meta[fid]||{}).path||'id '+fid}}
Aún no hay miembros. Buscá arriba y tickeá los archivos que querés agrupar.
📌 Notas y rutas manuales ({{groupNotes.length}})
Para paths que NO subiste (binarios .bmd, archivos del cliente, etc) y/o observaciones que la IA debe tener en cuenta sobre este grupo. Se guardan al hacer "{{groupForm.id?'Guardar cambios':'Crear grupo'}}".
No hay notas adicionales. Agregá rutas o observaciones que la IA debe considerar.

📄 Convertir cluster en doc

Revisá la respuesta canónica antes de guardarla como documento permanente. Al guardar, el cluster queda marcado como resolved y el doc se activa automáticamente.

{{kbForm.id?'✏️ Editar Doc':'➕ Nuevo Doc'}}

📎
{{kbFileProcessing?'⏳ Procesando...':'Subir archivo o arrastrá acá'}}
.md · .txt · .pdf (el texto se agrega al final del contenido)
Activo (se inyecta en el chat)
⚠️ Este doc tiene ~{{Math.round(kbForm.content.length/4).toLocaleString()}} tokens. Va a agregar costo y latencia a cada consulta. Considerá dividirlo.

🔍 Detalle de consulta

⏳ Cargando...

{{correctionForm.id?'✏️ Editar corrección #'+correctionForm.id:'📝 Nueva corrección'}}

Esta corrección va a aplicarse a preguntas similares como ejemplo few-shot, ayudando a la IA a responder en el mismo estilo.

{{correctionForm.original_response}}
{{f.side==='server'?'S':'E'}} {{f.path}}
{{p}}
{{f.side==='server'?'S':'E'}} {{f.path}}
{{p}}

🔐 Cambiar contraseña

Mínimo 10 caracteres, con mayúsculas, minúsculas y al menos un número.

{{chpw.error}}