rag-service/RAG/public/playground/index.html

297 lines
14 KiB
HTML

<!doctype html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RAG Playground</title>
<link rel="stylesheet" href="/playground/styles.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ignore/5.3.1/ignore.min.js"></script>
</head>
<body>
<main class="layout">
<header class="hero">
<div class="hero-brand">
<img class="brand-logo" src="https://por-correo.com/templates/yootheme/cache/e5/logo_por-correo-e574d3f1.webp" alt="POR-CORREO" />
<div>
<p class="eyebrow">RAG Playground</p>
<h1>Laboratorio de pruebas sistema RAG</h1>
<p class="lead">Prueba ingesta, bootstrap y chat con contexto visible para evaluar el RAG como si estuviera integrado en una app o agente real. Incluye comparacion con y sin RAG, seleccion de modelos, logs automáticos y logs solicitados por el usuario para analizar fallos, carencias y mejoras.</p>
</div>
</div>
<div class="hero-side">
<div class="log-counter" id="logCounterCard">
<span class="log-counter-value" id="logCounterValue">0</span>
<span class="log-counter-label">logs detectados</span>
</div>
<button id="healthButton" class="secondary">Comprobar health</button>
</div>
</header>
<section class="tabs">
<button class="tab-button active" data-tab="ingest">Ingesta</button>
<button class="tab-button" data-tab="cleanup">Limpieza</button>
<button class="tab-button" data-tab="bootstrap">Bootstrap</button>
<button class="tab-button" data-tab="chat">Chat</button>
</section>
<section id="tab-ingest" class="tab-panel active">
<article class="panel">
<h2>Ingesta</h2>
<div class="grid single-grid">
<label>Crear scope aislado
<select id="ingestScopeMode">
<option value="existing">usar generacion automatica</option>
<option value="custom">definir sourceId propio</option>
</select>
</label>
<label id="ingestSourceIdWrapper">Source ID / scope custom
<input id="ingestSourceId" placeholder="ej: src:cliente-a:manual:pdf-tecnico" />
</label>
<label>Tipo de fuente
<select id="ingestSourceType">
<option value="folder">folder</option>
<option value="file">file</option>
</select>
</label>
<label>Ruta de la fuente (remota en servidor)
<input id="ingestSourceRef" value="/home/pancho/Documentos/Empresa/Desarrollo/IA/docs" />
</label>
<label>Subida directa (sobreescribe la ruta de la fuente)
<div style="display: flex; gap: 8px; margin-top: 4px;">
<button id="btnUploadFile" class="secondary" style="flex: 1;">Archivo suelto</button>
<button id="btnUploadFolder" class="secondary" style="flex: 1;">Carpeta local</button>
</div>
<input id="ingestUploadFile" type="file" accept=".pdf,.md,.txt,.ts,.tsx,.js,.jsx,.mjs,.cjs,.py,.json,.yml,.yaml" style="display: none;" />
<input id="ingestUploadFolder" type="file" webkitdirectory directory multiple style="display: none;" />
<span id="uploadStatusText" style="display: block; margin-top: 8px; font-size: 13px; color: var(--accent);">Ningún elemento seleccionado</span>
</label>
<label>Modo de ingesta
<select id="ingestMode">
<option value="mechanical">mechanical</option>
<option value="interactive">interactive</option>
</select>
</label>
<label>Tags (coma separada)
<input id="ingestTags" value="workspace,global-docs" />
</label>
</div>
<p class="helper" id="ingestModeHint">Si seleccionas un archivo local, el playground usara upload directo y podras aislarlo con un `sourceId` propio para no mezclarlo con otros scopes.</p>
<div class="actions">
<button id="ingestButton">Lanzar ingesta</button>
</div>
<pre id="ingestResult">Sin ejecutar aun.</pre>
</article>
</section>
<section id="tab-cleanup" class="tab-panel">
<article class="panel">
<h2>Limpieza controlada</h2>
<p class="helper">Permite eliminar contexto ya ingerido del RAG antes de reingestar una fuente, para evitar duplicados y versiones mezcladas.</p>
<div class="grid single-grid">
<label>Scope disponible a limpiar
<select id="cleanupScopeSelect">
<option value="">Cargando scopes...</option>
</select>
</label>
<label>Source ID (obligatorio si no hay Source Ref)
<input id="cleanupSourceId" readonly />
</label>
<label>Source Ref (obligatorio si no hay Source ID)
<input id="cleanupSourceRef" readonly />
</label>
<label>Tags asociados (informativo)
<input id="cleanupTags" readonly />
</label>
</div>
<div class="actions">
<button id="cleanupButton" style="background: var(--danger); color: white;" title="Borrar de Qdrant los vectores del scope seleccionado" aria-label="Eliminar scope de Qdrant">Eliminar contexto seleccionado</button>
</div>
<pre id="cleanupResult">Selecciona un scope y pulsa el boton para eliminarlo.</pre>
</article>
</section>
<section id="tab-bootstrap" class="tab-panel">
<article class="panel">
<h2>Bootstrap de sesion</h2>
<div class="grid single-grid">
<label>Scope disponible
<select id="scopePresetSelect">
<option value="">Cargando scopes...</option>
</select>
</label>
<label>Source ID seleccionado
<input id="selectedSourceId" readonly />
</label>
<label>Scope por sourceRef
<input id="scopeSourceRef" value="/home/pancho/Documentos/Empresa/Desarrollo/IA/docs" />
</label>
<label>Tags (coma separada)
<input id="scopeTags" value="" />
</label>
<label>Modo del bootstrap
<select id="bootstrapMode">
<option value="documental">documental</option>
<option value="codigo">codigo</option>
<option value="auto">auto</option>
</select>
</label>
<label>Edicion manual del scope
<select id="scopeEditMode">
<option value="locked">bloqueado al scope seleccionado</option>
<option value="manual">editar manualmente</option>
</select>
</label>
<label>Modelo para sintetizar bootstrap
<select id="answerModel">
<option value="openai/gpt-4.1-mini">openai/gpt-4.1-mini</option>
</select>
</label>
<label>Consulta bootstrap
<textarea id="bootstrapQuery" rows="4">dame un mapa inicial del workspace, sus lineas de trabajo principales, reglas, documentacion base y puntos importantes a tener presentes</textarea>
</label>
</div>
<label class="checkbox">
<input type="checkbox" id="useModelInRetrieve" checked />
Usar un modelo para sintetizar el bootstrap recuperado
</label>
<div class="actions">
<button
id="bootstrapButton"
title="Ejecutar bootstrap y cargar contexto inicial"
aria-label="Ejecutar bootstrap y cargar contexto inicial"
>Cargar bootstrap</button>
<button
id="replaceBootstrapButton"
class="secondary"
title="Reemplazar el contexto activo por un nuevo bootstrap"
aria-label="Reemplazar el contexto activo por un nuevo bootstrap"
>Reemplazar contexto</button>
<button
id="clearBootstrapButton"
class="secondary"
title="Vaciar el contexto bootstrap activo"
aria-label="Vaciar el contexto bootstrap activo"
>Vaciar contexto</button>
<button
id="presetDocs"
class="secondary"
title="Aplicar preset para la documentacion global del workspace"
aria-label="Aplicar preset para la documentacion global del workspace"
>Preset docs</button>
<button
id="presetRagDocs"
class="secondary"
title="Aplicar preset para la documentacion del modulo RAG"
aria-label="Aplicar preset para la documentacion del modulo RAG"
>Preset RAG docs</button>
<button
id="presetCode"
class="secondary"
title="Aplicar preset para el codigo fuente del modulo RAG"
aria-label="Aplicar preset para el codigo fuente del modulo RAG"
>Preset codigo</button>
</div>
<div class="bootstrap-help" aria-label="Ayuda de uso de botones bootstrap">
<p class="helper bootstrap-help-summary">Los presets solo preparan la configuracion recomendada. Para cargar realmente el contexto en la sesion debes pulsar <code>Cargar bootstrap</code>. Si ya hay un contexto activo y quieres sustituirlo, usa <code>Reemplazar contexto</code>. Si quieres eliminarlo por completo, usa <code>Vaciar contexto</code>.</p>
<div class="bootstrap-help-grid">
<p><strong>Cargar bootstrap:</strong> Carga el contexto inicial del scope seleccionado para que el chat pueda trabajar con una panoramica del contenido.</p>
<p><strong>Reemplazar contexto:</strong> Recalcula el bootstrap y reemplaza el contexto cargado por uno nuevo usando la configuracion actual.</p>
<p><strong>Vaciar contexto:</strong> Borra el contexto precargado de la sesion para probar el chat sin bootstrap.</p>
<p><strong>Preset docs:</strong> Prepara el formulario para trabajar con la documentacion global del workspace. Despues pulsa <code>Cargar bootstrap</code> si quieres cargar ese contexto.</p>
<p><strong>Preset RAG docs:</strong> Prepara el formulario para trabajar con la documentacion del modulo RAG. Despues pulsa <code>Cargar bootstrap</code> si quieres cargar ese contexto.</p>
<p><strong>Preset codigo:</strong> Prepara el formulario para trabajar con el codigo del RAG en modo <code>codigo</code>. Despues pulsa <code>Cargar bootstrap</code> si quieres cargar ese contexto.</p>
</div>
</div>
<pre id="bootstrapResult">Sin ejecutar aun.</pre>
</article>
</section>
<section id="tab-chat" class="tab-panel">
<div class="grid chat-grid">
<article class="panel context-panel">
<h2>Estado del contexto</h2>
<div class="context-status" id="contextStatusCard">
<span id="contextIndicator" class="indicator indicator-off"></span>
<div>
<strong id="contextStatusText">Sin contexto cargado</strong>
<p id="contextScopeText">No hay bootstrap activo.</p>
</div>
</div>
<label>Scope activo para chat
<input id="chatScopeInfo" readonly />
</label>
<label class="checkbox">
<input type="checkbox" id="reuseBootstrapContext" checked />
Reutilizar bootstrap como contexto base
</label>
<label class="checkbox">
<input type="checkbox" id="allowAdditionalRetrieve" checked />
Permitir que el modelo haga consultas adicionales al RAG durante la conversacion
</label>
<label>Modo de chat
<select id="chatMode">
<option value="documental">documental</option>
<option value="codigo">codigo</option>
<option value="auto">auto</option>
</select>
</label>
<h3>Contexto bootstrap cargado</h3>
<pre id="bootstrapContextResult">Aun no hay bootstrap cargado.</pre>
</article>
<article class="panel chat-panel">
<h2>Chat con el modelo</h2>
<div id="chatMessages" class="chat-messages">
<p class="empty-chat">Aun no hay conversacion. Carga un bootstrap y empieza a preguntar.</p>
</div>
<label>Tu mensaje
<textarea id="chatInput" rows="4">que tenemos pendiente por hacer en este workspace</textarea>
</label>
<div class="actions">
<button id="sendChatButton">Enviar mensaje</button>
<button id="clearChatButton" class="secondary">Limpiar chat</button>
</div>
<label>Nota opcional para log manual
<textarea id="manualLogNote" rows="3" placeholder="Ejemplo: la respuesta no usa bien el contexto del libro y parece demasiado generica"></textarea>
</label>
<div class="actions">
<button id="manualLogButton" class="secondary">Registrar esta consulta en logs</button>
</div>
<h3>Ultima respuesta estructurada</h3>
<pre id="mainResult">Sin ejecutar aun.</pre>
<h3>Comparacion sin RAG</h3>
<label class="checkbox">
<input type="checkbox" id="compareWithoutRag" />
Comparar tambien con respuesta sin RAG
</label>
<pre id="compareResult">Desactivada.</pre>
</article>
</div>
</section>
<section class="panel">
<h2>Estado / health</h2>
<pre id="healthResult">Sin comprobar.</pre>
</section>
<section class="panel">
<h2>Logs recientes</h2>
<pre id="logsResult">Sin cargar aun.</pre>
</section>
</main>
<script src="/playground/app.js" type="module"></script>
</body>
</html>