/* INSPECTION VIEW — trace a real query through the retrieval pipeline.
Runs POST /query and shows the actual route decision, the ranked candidates
with their real scores, and the assembled context. The pipeline strip labels
each stage with real values; stages the backend doesn't expose internals for
(embedding, the gate distribution) are described, not fabricated. */
function inspToCand(c) {
const pages = c.page_numbers || [];
return {
chunk_id: c.chunk_id,
paper: c.paper_id,
page: pages[0],
score: typeof c.score === "number" ? c.score : 0,
kind: c.source === "visual" ? "visual" : "text",
bbox: (c.metadata && c.metadata.bbox) || null,
text: c.text || "",
};
}
function Stage({ icon, label, value, sub, last, selected, onClick }) {
return (
{label}
{value}
{sub && {sub}
}
{!last &&
}
);
}
function InspRow({ c, onOpen, scoreMin, scoreMax }) {
// Min-max scaled within the result set — logits carry any sign.
const rel = scoreMax === scoreMin ? 1 : ((c.score || 0) - scoreMin) / (scoreMax - scoreMin);
return (
onOpen(c)} title="View source region on page">
{c.kind === "visual" ? "IMG" : "TXT"}
{c.paper} · p.{c.page}
relevance
{c.score.toFixed(3)}
{c.text &&
{clip(c.text, 150)}
}
);
}
function InspectionView({ settings, papers, routingAvailable }) {
const [draft, setDraft] = useState(window.RAG.SUGGESTIONS[0].q);
const [busy, setBusy] = useState(false);
const [status, setStatus] = useState("");
const [result, setResult] = useState(null); // { query, cands, routing }
const [activeStage, setActiveStage] = useState(null);
const [pageItem, setPageItem] = useState(null);
const paperTitle = useCallback(
(id) => { const p = (papers || []).find((x) => x.paper_id === id); return (p && p.title) || id; },
[papers]
);
const trace = useCallback(async (text) => {
const v = (text != null ? text : draft).trim();
if (!v || busy) return;
setBusy(true); setStatus(""); setActiveStage(null);
try {
const { results, routing } = await window.RAG.retrieve(v, {
topK: settings.topk,
forceRoute: settings.route === "text" ? "text" : settings.route === "visual" ? "hybrid" : "",
routingMode: settings.routingMode || "",
paperId: settings.paper || "",
onStatus: setStatus,
});
setStatus("");
setResult({ query: v, cands: results.map(inspToCand), routing });
} catch (err) {
setStatus(`Trace failed: ${(err && err.message) || err}`);
setResult(null);
} finally {
setBusy(false);
}
}, [draft, busy, settings]);
const cands = result ? result.cands : [];
const textCands = cands.filter((c) => c.kind === "text");
const visCands = cands.filter((c) => c.kind === "visual");
// Min-max scale within the set: rerank logits carry any sign, and an
// all-negative set must not render every bar empty.
const allScores = cands.map((c) => c.score || 0);
const scoreMax = allScores.length ? Math.max(...allScores) : 0;
const scoreMin = allScores.length ? Math.min(...allScores) : 0;
const routeLabel = result ? window.RAG.routeLabel(result.routing) : "text";
const stageInfo = {
query: { icon: "text", title: "Query", body: result ? `Normalized query: “${result.query}”` : "The turn, resolved against conversation history and classified by intent." },
embed: { icon: "layers", title: "Embed", body: "Encoded with bge-m3 into a 1024-d dense vector (L2-normalized) for nearest-neighbour search. The raw vector isn't surfaced by the API." },
route: { icon: "route", title: "Route gate", body: routingAvailable === false
? "No router on this deployment — every query retrieves text-side. The gate runs where the GPU visual leg is built."
: result ? `Routing mode: ${result.routing?.mode || settings.routingMode || "default"} · path: ${result.routing?.path || "text"}${result.routing?.forced ? " · forced" : ""}${result.routing?.category ? " · category: " + result.routing.category : ""}` : "A gate predicts which store(s) hold the answer." },
retrieve: { icon: "search", title: "Retrieve", body: result ? `Top ${cands.length} after reranking the hybrid BM25 + dense (RRF-fused) candidate pool (${textCands.length} text, ${visCands.length} visual).` : "Pulls top-k candidates from each enabled store." },
rerank: { icon: "filter", title: "Rerank", body: "A MiniLM cross-encoder re-scores the candidate pool; the score shown on each row is the post-rerank relevance." },
assemble: { icon: "check", title: "Assemble", body: result ? `Top ${cands.length} chunks packed into the generation context (budget ${settings.topk}).` : "Greedily packs the highest-scoring chunks under a token budget." },
};
return (
setDraft(e.target.value)}
onKeyDown={(e) => { if (e.key === "Enter") trace(); }} />
trace()} disabled={!draft.trim() || busy}>
{busy ? "Tracing…" : "Trace"}
{result && routingAvailable !== false &&
}
{settings.paper &&
filter: {settings.paper}
}
EXAMPLES
{window.RAG.SUGGESTIONS.map((s, i) => (
{ if (!busy) { setDraft(s.q); trace(s.q); } }}>{clip(s.q, 38)}
))}
{status &&
{status}
}
{!result && !status &&
Run a trace to see the real routing decision, ranked candidates, and assembled context.
}
{result && (
setActiveStage((a) => a === "query" ? null : "query")} />
setActiveStage((a) => a === "embed" ? null : "embed")} />
setActiveStage((a) => a === "route" ? null : "route")} />
setActiveStage((a) => a === "retrieve" ? null : "retrieve")} />
setActiveStage((a) => a === "rerank" ? null : "rerank")} />
setActiveStage((a) => a === "assemble" ? null : "assemble")} />
{activeStage && stageInfo[activeStage] && (
{stageInfo[activeStage].title} {stageInfo[activeStage].body}
setActiveStage(null)}>
)}
Text store{textCands.length}
{textCands.length ? textCands.map((c, i) =>
) :
No text candidates.
}
Visual store{routingAvailable === false ? "off" : visCands.length}
{visCands.length
? visCands.map((c, i) =>
)
:
{routingAvailable === false
?
Not built on this deployment — the visual leg needs a GPU. Offline it measures +35% recall@10 over text-only retrieval (results ); here retrieval runs text-side and figure questions are answered from page images at generation time.
: "No visual candidates passed the gate for this query."}
}
Assembled context {cands.length} chunks
{cands.map((c, i) => (
setPageItem(c)} title="View source region on page">
{i + 1}
{c.kind === "visual" ? "IMG" : "TXT"}
{c.paper} · p.{c.page}
region
{c.score.toFixed(3)}
))}
)}
setPageItem(null)} paperTitle={paperTitle} />
);
}
window.InspectionView = InspectionView;