fix: a deck asked for a figure and never got one — three causes, one symptom
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 52s
Forgejo Docker Build / Root app tests (push) Successful in 52s
Forgejo Android APK / Build signed APK (push) Successful in 2m15s
Forgejo Docker Build / Build Docker image (push) Successful in 10s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 52s
Forgejo Docker Build / Root app tests (push) Successful in 52s
Forgejo Android APK / Build signed APK (push) Successful in 2m15s
Forgejo Docker Build / Build Docker image (push) Successful in 10s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
"Include a diagram" produced decks with no picture. Three separate faults, each hiding the next, found by generating the same deck after fixing each one. First, deck generation ran on the default 4000-token budget. A deck's JSON is several times the size of the prose it holds, so a long deck came back truncated, failed to parse, and fell back to markdown — which has no way to request a figure, so the model described one instead and the slide rendered a literal "![Placeholder: Flow diagram ...]" as its first bullet. Deck generation now gets room, and the fallback says how the reply failed: empty, cut short at N characters, or not a deck. Second, the figure request sat inside the layout vocabulary, one line among forty, and the model passed over it. It goes last now, after the author's own instructions — the same placement lesson the image tool taught earlier. Third, and the one that actually mattered: image_prompt is only read on the figure and image types, so an image_prompt on a bullets slide was dropped in silence. The instruction said "add image_prompt to N slides" without saying which types carry one. It now names them, and a misplaced request is honoured rather than discarded — a slide with words becomes a figure, one without becomes a full-slide image. Image markup is also stripped wherever text enters a slide, on both paths: a described figure is not a figure, and a bullet of raw markdown is worse than no bullet. Verified end to end after: the same request produced a deck with one figure, the job completed, and the exported pptx carries one embedded image across 21 slides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
This commit is contained in:
parent
2c3fbbcf37
commit
5614a436be
3 changed files with 56 additions and 2 deletions
|
|
@ -132,11 +132,29 @@ function buildPrompt(opts) {
|
|||
// has no way to ask for any of them. Articles stay markdown, which is the
|
||||
// right shape for prose.
|
||||
if (kind === 'presentation' && opts.deckMode) {
|
||||
// The figure request goes last, after the author's own instructions. Inside
|
||||
// the layout vocabulary it was one line among forty and the model passed
|
||||
// over it: a twelve-slide deck asked to "include a diagram of the diagnostic
|
||||
// pathway" came back with no image_prompt at all. Placement was the same
|
||||
// lesson the tool path taught — whatever must not be missed goes last.
|
||||
var figures = !opts.wantsImages ? '' : '\n\n' + (opts.figureCount
|
||||
? 'Before you finish: this deck must carry ' + opts.figureCount + ' figure' +
|
||||
(opts.figureCount === 1 ? '' : 's') + '. Make ' + opts.figureCount + ' of the slides ' +
|
||||
'type "figure" (bullets on the left, the picture on the right) or type "image" (the ' +
|
||||
'picture is the whole slide), and give each one an "image_prompt" describing what to ' +
|
||||
'draw. Those two types are the only ones that carry a picture. A deck returned without ' +
|
||||
(opts.figureCount === 1 ? 'it' : 'them') + ' has not followed the instruction.'
|
||||
: 'Before you finish: where a diagram or labelled figure would genuinely help, make that ' +
|
||||
'slide type "figure" or type "image" and give it an "image_prompt" describing what to ' +
|
||||
'draw. Those two types are the only ones that carry a picture. Schematic or anatomical ' +
|
||||
'teaching artwork only, never a real patient.');
|
||||
|
||||
return 'You are building a teaching presentation for a medical professional ' +
|
||||
'audience (pediatrics / primary care).\n\nTOPIC: ' + opts.topic + '\n' +
|
||||
grounding + findings + '\n' +
|
||||
deckSchema.instructions(opts.slideCount, opts.figureCount) +
|
||||
(opts.refinement ? '\n\nAdditional instructions: ' + opts.refinement + '\n' : '');
|
||||
(opts.refinement ? '\n\nAdditional instructions: ' + opts.refinement + '\n' : '') +
|
||||
figures;
|
||||
}
|
||||
|
||||
var shape = kind === 'presentation'
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ function instructions(slideCount, figureCount) {
|
|||
slideShapes.instructions(),
|
||||
'',
|
||||
figureCount
|
||||
? 'Include exactly ' + figureCount + ' slides carrying an image_prompt, spread through the deck.'
|
||||
? 'Include exactly ' + figureCount + ' slide' + (figureCount === 1 ? '' : 's') +
|
||||
' carrying an image_prompt, spread through the deck.'
|
||||
: 'Include an image_prompt only where a picture genuinely earns its place, and at most three.',
|
||||
'',
|
||||
'Vary the layouts: a deck of nothing but "bullets" is the thing to avoid.',
|
||||
|
|
@ -137,6 +138,17 @@ function normalise(raw, gaps) {
|
|||
if (type === 'bullets' && !out.bullets.length && !out.heading) return;
|
||||
}
|
||||
|
||||
// An image_prompt on a slide that does not carry pictures used to be dropped
|
||||
// in silence, so a model that asked for a figure the wrong way got none and
|
||||
// no one found out. The intent is honoured instead: a slide with words
|
||||
// becomes a figure, one without becomes a full-slide image.
|
||||
if (slide.image_prompt && type !== 'figure' && type !== 'image'
|
||||
&& type !== 'title' && type !== 'custom') {
|
||||
out.type = type = (out.bullets && out.bullets.length) || (slide.bullets || []).length
|
||||
? 'figure' : 'image';
|
||||
if (type === 'figure' && !(out.bullets || []).length) out.bullets = bullets(slide.bullets);
|
||||
}
|
||||
|
||||
// A figure request is a prompt, not a path. The route turns the ones it can
|
||||
// afford into jobs and hands back files; anything unfulfilled degrades to a
|
||||
// slide of text rather than an empty frame.
|
||||
|
|
|
|||
|
|
@ -218,3 +218,27 @@ test('a deck is given room to be a deck', () => {
|
|||
assert.match(route, /cut short at ' \+ replyLength \+ ' chars'/);
|
||||
assert.match(route, /empty reply/);
|
||||
});
|
||||
|
||||
test('a figure asked for the wrong way is honoured, not dropped', () => {
|
||||
// image_prompt is only carried by the figure and image types, so a model that
|
||||
// put one on a bullets slide had it silently discarded — it asked for a
|
||||
// picture, got none, and nothing said why. Measured on a real generation: a
|
||||
// twenty-slide deck, an explicit "include a diagram", zero image_prompt.
|
||||
const deck = deckSchema.normalise({ slides: [
|
||||
{ type: 'bullets', heading: 'Pathway', bullets: ['a', 'b'], image_prompt: 'a flow diagram' },
|
||||
{ type: 'section', heading: 'Overview', image_prompt: 'a full-bleed picture' },
|
||||
]});
|
||||
// Words beside a picture is a figure; no words is a full-slide image.
|
||||
assert.equal(deck.slides[0].type, 'figure');
|
||||
assert.equal(deck.slides[0].image_prompt, 'a flow diagram');
|
||||
assert.equal(deck.slides[1].type, 'image');
|
||||
|
||||
// And the instruction now names the only two types that carry one, last in
|
||||
// the prompt, after the author's own instructions.
|
||||
const route = read('src/routes/myResources.js');
|
||||
assert.match(route, /Those two types are the only ones that carry a picture/);
|
||||
const deckBranch = route.slice(route.indexOf("if (kind === 'presentation' && opts.deckMode)"));
|
||||
const built = deckBranch.indexOf('var figures =');
|
||||
const used = deckBranch.indexOf("+\n figures;");
|
||||
assert.ok(built > -1 && used > built, 'the figure request is appended last');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue