Hand a text-only model a PDF's text as text, not as an attachment

Headed "Attached document", the text of a PDF read for a model that
takes only text made that model answer it could not open files: it read
the heading as a file it had not been given and never got to the text
under it. The heading now says the full text is included, between two
markers, to be read directly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lucio Lelii 2026-09-25 18:35:32 +02:00
parent 9f4f62f941
commit acd43f8f1e
2 changed files with 19 additions and 11 deletions

View File

@ -167,28 +167,35 @@ public class LLMAttachmentPreparer {
return null;
}
/**
* The document's text, said to be exactly that. Headed as an attachment, a model that takes
* only text answers that it cannot open files - it reads the heading as a file it was not
* given, and never gets to the text right under it.
*/
private String documentBlock(String name, ReadPdf read) {
String nl = System.lineSeparator();
StringBuilder block = new StringBuilder();
block.append(System.lineSeparator()).append(System.lineSeparator())
.append("--- Attached document: ").append(name).append(" (").append(read.pageCount())
.append(read.pageCount() == 1 ? " page" : " pages").append(") ---").append(System.lineSeparator());
block.append(nl).append(nl)
.append("You were given the document ").append(name).append(" (").append(read.pageCount())
.append(read.pageCount() == 1 ? " page" : " pages").append("). ");
if (read.hasText()) {
block.append(read.text()).append(System.lineSeparator());
block.append("Its full text is included here, between the two markers, so you can read it directly:")
.append(nl).append("<<<BEGIN ").append(name).append(">>>").append(nl)
.append(read.text()).append(nl);
if (read.textTruncated()) {
block.append("[The text is cut here: the document is longer than can be sent.]")
.append(System.lineSeparator());
block.append("[The text is cut here: the document is longer than can be sent.]").append(nl);
}
block.append("<<<END ").append(name).append(">>>");
} else {
block.append("[This document has no text layer; read it from its page images.]").append(System.lineSeparator());
block.append("It has no text layer, so read it from its page images.");
}
if (!read.pageImages().isEmpty()) {
block.append("[Its first ").append(read.pageImages().size())
block.append(nl).append("Its first ").append(read.pageImages().size())
.append(read.pageImages().size() == 1 ? " page is" : " pages are")
.append(" also attached as images")
.append(read.pageImages().size() < read.pageCount() ? ", of " + read.pageCount() : "")
.append(".]").append(System.lineSeparator());
.append(".");
}
block.append("--- End of ").append(name).append(" ---");
return block.toString();
}

View File

@ -120,7 +120,8 @@ class LLMAttachmentPreparerTest {
LLMAttachmentPreparer.Prepared prepared = preparer.prepare(new FileAttachments(List.of(), List.of(report)),
"summarise", provider(Set.of(ChatAttachment.Kind.IMAGE), true), "m", null);
assertTrue(prepared.documentText().contains("--- Attached document: report.pdf (3 pages) ---"), prepared.documentText());
assertTrue(prepared.documentText().contains("You were given the document report.pdf (3 pages)."), prepared.documentText());
assertTrue(prepared.documentText().contains("<<<BEGIN report.pdf>>>"), prepared.documentText());
assertTrue(prepared.documentText().contains("Quarterly revenue grew"), prepared.documentText());
assertTrue(prepared.documentText().contains("first 2 pages are also attached as images, of 3"), prepared.documentText());
assertEquals(List.of("report.pdf-page-1.jpg", "report.pdf-page-2.jpg"),