Skip to content

Commit 192d9e9

Browse files
fix(web): ground named-figure answers in their own caption
Asking about Figure 1 retrieved body text about Fig. 2 ("Fig. 2 illustrates the overall framework..." ranks high on framework questions while the Fig. 1 caption shares almost no words with the question), and the model transplanted that sentence onto Figure 1. The figure index already resolves the named element to attach its page image; its caption now also enters the context as a citable chunk, so the asked figure is always grounded by its own text. The prompt forbids carrying claims across figure numbers, and the new citation resolves through the figure index - the chip opens the source page with the figure region box.
1 parent cb11f1f commit 192d9e9

2 files changed

Lines changed: 33 additions & 13 deletions

File tree

web/app/api.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@
243243
for (const paperId of papers) {
244244
const f = figureIndex.find((g) => g.paper_id === paperId && re.test(String(g.caption || "").trim()));
245245
if (f && typeof f.page_number === "number") {
246-
out.push({ paperId, page: f.page_number });
246+
out.push({ paperId, page: f.page_number, chunkId: f.chunk_id, caption: String(f.caption || "") });
247247
// One page per reference — matching the same "Figure 2" in a second
248248
// paper would crowd out the question's other references.
249249
break;
@@ -266,6 +266,7 @@
266266
"- Cite specific chunk IDs when making factual claims by wrapping the literal id in square brackets. Example: if a chunk header is [2604.22753v1::p5::c24], cite it as [2604.22753v1::p5::c24] — NOT [chunk_id 2604.22753v1::p5::c24] and NOT [chunk 24]. Use only ids that appear in the provided context.",
267267
"- Attached page images are labeled with their own id, e.g. [page image 2604.22753v1::p5::page]. When you describe what a figure, plot, table, or diagram shows based on looking at a page image, cite that page id (e.g. [2604.22753v1::p5::page]) — not a text chunk. Cite text chunk ids only for claims supported by the chunk text itself.",
268268
"- Several pages may be attached. Cite the id of the page that actually contains the figure you are describing — check the label immediately before the image you read; a page that merely mentions the figure in its text is the wrong citation.",
269+
"- Watch figure numbers. If a retrieved chunk discusses a different figure than the one the user asked about (it says \"Fig. 2\" but the question asks about Figure 1), do not transfer its claims to the asked figure. Describe the asked figure only from its own caption chunk or its page image.",
269270
"- Prior turns are included for reference. If the user follows up about something from your own previous answer (a term you used, a claim you made) and the current chunks don't cover it, explain it from the previous turn's evidence — without bracket citations — instead of refusing.",
270271
"- Keep answers concise (3-6 sentences unless the question demands more).",
271272
].join("\n");
@@ -304,18 +305,26 @@
304305
}
305306
}
306307
}
307-
// Attach the page that actually shows a figure/table the question names,
308-
// when retrieval didn't already bring it in.
309-
if (useImages) {
310-
for (const ref of referencedFigurePages(latestUserText, chunks, figureIndex)) {
311-
const key = `${ref.paperId}:${ref.page}`;
312-
if (seenPages.has(key)) continue;
313-
seenPages.add(key);
314-
const dataUrl = await imageToDataUrl(pageImageUrl(ref.paperId, ref.page));
315-
if (dataUrl) {
316-
content.push({ type: "text", text: `[page image ${ref.paperId}::p${ref.page}::page]` });
317-
content.push({ type: "image_url", image_url: { url: dataUrl } });
318-
}
308+
// When the question names a figure/table, inject its caption as a citable
309+
// chunk and attach its page. Retrieval often misses the caption chunk —
310+
// captions rarely share words with the question ("Fig. 1: (a) Previous
311+
// driving world models…" vs "What does Figure 1 illustrate?") — and the
312+
// model then transplants text about a DIFFERENT figure onto the asked one.
313+
for (const ref of referencedFigurePages(latestUserText, chunks, figureIndex)) {
314+
if (ref.chunkId && ref.caption) {
315+
content.push({
316+
type: "text",
317+
text: `[chunk ${ref.chunkId}] paper=${ref.paperId} pages=${ref.page} — caption of the figure/table named in the question\n${ref.caption.slice(0, 700)}`,
318+
});
319+
}
320+
if (!useImages) continue;
321+
const key = `${ref.paperId}:${ref.page}`;
322+
if (seenPages.has(key)) continue;
323+
seenPages.add(key);
324+
const dataUrl = await imageToDataUrl(pageImageUrl(ref.paperId, ref.page));
325+
if (dataUrl) {
326+
content.push({ type: "text", text: `[page image ${ref.paperId}::p${ref.page}::page]` });
327+
content.push({ type: "image_url", image_url: { url: dataUrl } });
319328
}
320329
}
321330
content.push({ type: "text", text: `\nQuestion: ${latestUserText}` });

web/app/chat.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,12 @@ function ChatView({ settings, set, layout, resetSignal, apiKey, model, papers, f
441441
const pm = id.match(/^(.+)::p(\d+)::page$/);
442442
if (pm) return { n: i + 1, id, paper: pm[1], page: +pm[2], quote: null, kind: "visual", page_cite: true };
443443
const c = byId.get(id);
444+
if (!c) {
445+
// Injected figure/table caption (buildMessages adds it when the
446+
// question names the element) — resolve through the figure index.
447+
const fg = (figures || []).find((g) => g.chunk_id === id);
448+
if (fg) return { n: i + 1, id, paper: fg.paper_id, page: fg.page_number, quote: previewQuote(fg.caption || ""), kind: "visual", fig_cite: true, bbox: fg.bbox || null };
449+
}
444450
const pages = c ? c.page_numbers || [] : [];
445451
return { n: i + 1, id, paper: c ? c.paper_id : id, page: pages[0], quote: c ? previewQuote(c.text) : null, kind: c && c.source === "visual" ? "visual" : "text" };
446452
});
@@ -502,6 +508,11 @@ function ChatView({ settings, set, layout, resetSignal, apiKey, model, papers, f
502508
setPageItem({ chunk_id: cit.id, paper: cit.paper, page: cit.page, pages: [cit.page], kind: "visual", bbox: null, text: "", page_cite: true });
503509
return;
504510
}
511+
if (cit && cit.fig_cite) {
512+
// Injected figure caption: open its page with the region box.
513+
setPageItem({ chunk_id: cit.id, paper: cit.paper, page: cit.page, pages: [cit.page], kind: "visual", bbox: cit.bbox || null, text: cit.quote || "" });
514+
return;
515+
}
505516
// The panel only ever shows the LAST turn's evidence, so a highlight is
506517
// wrong for citations in older messages — open the modal for those too.
507518
const panelHidden = layout === "single" || (window.matchMedia && window.matchMedia("(max-width: 760px)").matches);

0 commit comments

Comments
 (0)