Improve scope handling in RAG playground

This commit is contained in:
Paco POR-CORREO 2026-04-06 18:56:53 +02:00
parent 6557aea0bc
commit 02798085b1
2 changed files with 71 additions and 14 deletions

View file

@ -21,6 +21,8 @@ const contextStatusText = document.getElementById("contextStatusText");
const contextScopeText = document.getElementById("contextScopeText");
const ingestSourceType = document.getElementById("ingestSourceType");
const ingestScopeMode = document.getElementById("ingestScopeMode");
const ingestSourceIdWrapper = document.getElementById("ingestSourceIdWrapper");
const ingestSourceId = document.getElementById("ingestSourceId");
const ingestSourceRef = document.getElementById("ingestSourceRef");
const ingestUploadFile = document.getElementById("ingestUploadFile");
@ -35,14 +37,18 @@ const useModelInRetrieve = document.getElementById("useModelInRetrieve");
const reuseBootstrapContext = document.getElementById("reuseBootstrapContext");
const allowAdditionalRetrieve = document.getElementById("allowAdditionalRetrieve");
const scopePresetSelect = document.getElementById("scopePresetSelect");
const selectedSourceId = document.getElementById("selectedSourceId");
const scopeSourceRef = document.getElementById("scopeSourceRef");
const scopeTags = document.getElementById("scopeTags");
const scopeEditMode = document.getElementById("scopeEditMode");
const compareWithoutRag = document.getElementById("compareWithoutRag");
const chatMode = document.getElementById("chatMode");
const chatScopeInfo = document.getElementById("chatScopeInfo");
const chatInput = document.getElementById("chatInput");
let lastBootstrapContext = null;
let chatHistory = [];
let availableScopes = [];
function format(value) {
return JSON.stringify(value, null, 2);
@ -62,6 +68,7 @@ function updateIngestUiState() {
ingestSourceType.value = hasUpload ? "file" : ingestSourceType.value;
ingestSourceType.disabled = hasUpload;
ingestSourceRef.disabled = hasUpload;
ingestSourceIdWrapper.style.display = ingestScopeMode.value === "custom" ? "block" : "none";
if (hasUpload) {
ingestModeHint.textContent = `Upload directo activo: se ingerira el archivo local "${ingestUploadFile.files[0].name}" y se ignorara la ruta manual.`;
@ -72,6 +79,27 @@ function updateIngestUiState() {
}
}
function applySelectedScope(scope) {
selectedSourceId.value = scope?.sourceId || "";
scopeSourceRef.value = scope?.sourceRef || "";
scopeTags.value = (scope?.tags || []).join(", ");
chatScopeInfo.value = [scope?.sourceId, scope?.sourceRef].filter(Boolean).join(" | ");
if (scope?.chunkModes?.includes("codigo")) {
bootstrapMode.value = "codigo";
chatMode.value = "codigo";
} else if (scope?.chunkModes?.includes("documental")) {
bootstrapMode.value = "documental";
chatMode.value = "documental";
}
}
function updateScopeEditState() {
const locked = scopeEditMode.value !== "manual";
scopeSourceRef.readOnly = locked;
scopeTags.readOnly = locked;
}
function request(url, payload, method = "POST") {
return fetch(url, {
method,
@ -94,6 +122,7 @@ function renderBootstrapContext() {
contextIndicator.className = "indicator indicator-off";
contextStatusText.textContent = "Sin contexto cargado";
contextScopeText.textContent = "No hay bootstrap activo.";
chatScopeInfo.value = [selectedSourceId.value, scopeSourceRef.value].filter(Boolean).join(" | ") || "Sin scope seleccionado";
return;
}
@ -101,6 +130,7 @@ function renderBootstrapContext() {
contextIndicator.className = "indicator indicator-on";
contextStatusText.textContent = "Contexto bootstrap activo";
contextScopeText.textContent = lastBootstrapContext.scope?.sourceRef || "Scope no especificado";
chatScopeInfo.value = [lastBootstrapContext.scope?.sourceId, lastBootstrapContext.scope?.sourceRef].filter(Boolean).join(" | ");
}
function renderChatHistory() {
@ -120,7 +150,17 @@ function renderChatHistory() {
}
function buildScopeFromInputs() {
if (scopeEditMode.value !== "manual" && scopePresetSelect.value) {
const scope = JSON.parse(scopePresetSelect.value);
return {
sourceId: scope.sourceId,
sourceRef: scope.sourceRef,
tags: scope.tags || []
};
}
return {
sourceId: selectedSourceId.value || undefined,
sourceRef: scopeSourceRef.value,
tags: splitTags(scopeTags.value)
};
@ -137,6 +177,7 @@ function applyPreset(mode, query, sourceRef, useRetrieveModel = false) {
async function loadScopes() {
try {
const scopes = await fetch("/sources").then((response) => response.json());
availableScopes = scopes;
scopePresetSelect.innerHTML = "";
const placeholder = document.createElement("option");
@ -153,6 +194,9 @@ async function loadScopes() {
if (scopes.length === 0) {
placeholder.textContent = "No hay scopes detectados";
} else if (!scopePresetSelect.value) {
scopePresetSelect.value = JSON.stringify(scopes[0]);
applySelectedScope(scopes[0]);
}
} catch (error) {
scopePresetSelect.innerHTML = `<option value="">Error cargando scopes: ${error}</option>`;
@ -192,19 +236,12 @@ scopePresetSelect.addEventListener("change", () => {
}
const scope = JSON.parse(scopePresetSelect.value);
scopeSourceRef.value = scope.sourceRef || "";
scopeTags.value = (scope.tags || []).join(", ");
if (scope.chunkModes.includes("codigo")) {
bootstrapMode.value = "codigo";
chatMode.value = "codigo";
} else if (scope.chunkModes.includes("documental")) {
bootstrapMode.value = "documental";
chatMode.value = "documental";
}
applySelectedScope(scope);
});
ingestUploadFile.addEventListener("change", updateIngestUiState);
ingestScopeMode.addEventListener("change", updateIngestUiState);
scopeEditMode.addEventListener("change", updateScopeEditState);
healthButton.addEventListener("click", async () => {
healthResult.textContent = "Comprobando...";
@ -226,7 +263,7 @@ ingestButton.addEventListener("click", async () => {
formData.append("file", ingestUploadFile.files[0]);
formData.append("mode", ingestMode.value);
formData.append("tags", splitTags(ingestTags.value).join(","));
if (ingestSourceId.value.trim()) {
if (ingestScopeMode.value === "custom" && ingestSourceId.value.trim()) {
formData.append("sourceId", ingestSourceId.value.trim());
}
@ -240,7 +277,7 @@ ingestButton.addEventListener("click", async () => {
}
} else {
data = await request("/ingest", {
sourceId: ingestSourceId.value.trim() || undefined,
sourceId: ingestScopeMode.value === "custom" ? (ingestSourceId.value.trim() || undefined) : undefined,
sourceType: ingestSourceType.value,
sourceRef: ingestSourceRef.value,
mode: ingestMode.value,
@ -369,3 +406,4 @@ loadAnswerModels();
renderBootstrapContext();
renderChatHistory();
updateIngestUiState();
updateScopeEditState();

View file

@ -27,7 +27,13 @@
<article class="panel">
<h2>Ingesta</h2>
<div class="grid single-grid">
<label>Source ID / scope custom
<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
@ -69,6 +75,9 @@
<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>
@ -82,6 +91,12 @@
<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>
@ -111,7 +126,7 @@
<div class="grid chat-grid">
<article class="panel context-panel">
<h2>Estado del contexto</h2>
<div class="context-status" id="contextStatusCard">
<div class="context-status" id="contextStatusCard">
<span id="contextIndicator" class="indicator indicator-off"></span>
<div>
<strong id="contextStatusText">Sin contexto cargado</strong>
@ -119,6 +134,10 @@
</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