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

View file

@ -27,7 +27,13 @@
<article class="panel"> <article class="panel">
<h2>Ingesta</h2> <h2>Ingesta</h2>
<div class="grid single-grid"> <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" /> <input id="ingestSourceId" placeholder="ej: src:cliente-a:manual:pdf-tecnico" />
</label> </label>
<label>Tipo de fuente <label>Tipo de fuente
@ -69,6 +75,9 @@
<option value="">Cargando scopes...</option> <option value="">Cargando scopes...</option>
</select> </select>
</label> </label>
<label>Source ID seleccionado
<input id="selectedSourceId" readonly />
</label>
<label>Scope por sourceRef <label>Scope por sourceRef
<input id="scopeSourceRef" value="/home/pancho/Documentos/Empresa/Desarrollo/IA/docs" /> <input id="scopeSourceRef" value="/home/pancho/Documentos/Empresa/Desarrollo/IA/docs" />
</label> </label>
@ -82,6 +91,12 @@
<option value="auto">auto</option> <option value="auto">auto</option>
</select> </select>
</label> </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 <label>Modelo para sintetizar bootstrap
<select id="answerModel"> <select id="answerModel">
<option value="openai/gpt-4.1-mini">openai/gpt-4.1-mini</option> <option value="openai/gpt-4.1-mini">openai/gpt-4.1-mini</option>
@ -111,7 +126,7 @@
<div class="grid chat-grid"> <div class="grid chat-grid">
<article class="panel context-panel"> <article class="panel context-panel">
<h2>Estado del contexto</h2> <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> <span id="contextIndicator" class="indicator indicator-off"></span>
<div> <div>
<strong id="contextStatusText">Sin contexto cargado</strong> <strong id="contextStatusText">Sin contexto cargado</strong>
@ -119,6 +134,10 @@
</div> </div>
</div> </div>
<label>Scope activo para chat
<input id="chatScopeInfo" readonly />
</label>
<label class="checkbox"> <label class="checkbox">
<input type="checkbox" id="reuseBootstrapContext" checked /> <input type="checkbox" id="reuseBootstrapContext" checked />
Reutilizar bootstrap como contexto base Reutilizar bootstrap como contexto base