fix(directory-templates): switch concept-profile to sonar-pro; surface unreplaced image markers

Concept-profile was using sonar-deep-research, which the article-generator's
existing compatibility-warning has long flagged as "Images are unstable in
deep research mode" — Perplexity returns an empty images array, so the
[IMAGE N: …] markers the model emits never get swapped for embeds and the
template's image-placeholder text leaks into the output.

Two changes:

1. Concept-profile drops to sonar-pro to match vocabulary-profile. Both are
   encyclopedia-style entries that don't need the multi-step research loop;
   sonar-pro is also the model that reliably returns images.

2. Post-stream cleanup now counts [IMAGE N: …] markers before and after
   replacement. When markers remain unreplaced, log a console warning and
   show a Notice telling the user how many markers stayed and how many
   images Perplexity actually returned — so the failure mode is visible
   instead of silent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
mpstaton 2026-05-09 21:44:13 -05:00
parent 9c490413b0
commit ac29da3bc3
2 changed files with 18 additions and 1 deletions

View file

@ -13,7 +13,7 @@ # About this template
```cft
provider: perplexity
model: sonar-deep-research
model: sonar-pro
search-recency: year
return-citations: true
return-images: true

View file

@ -520,8 +520,25 @@ export async function applyTemplate(
const trimmedStreamed = streamed.replace(/^\s+/, '').replace(/\s+$/, '');
let cleanedStreamed = wrapThinkBlocks(trimmedStreamed);
if (wantsImages) {
const markerRe = /\[IMAGE\s+\d+:\s*[^\]]*?\]/gi;
const markersBefore = (cleanedStreamed.match(markerRe) ?? []).length;
cleanedStreamed = processContentWithImages(cleanedStreamed, images);
cleanedStreamed = stripUnreplacedImagePlaceholders(cleanedStreamed);
const markersAfter = (cleanedStreamed.match(markerRe) ?? []).length;
// Diagnostics: when the model emitted markers but Perplexity returned
// no images (common on sonar-deep-research), surface so the user knows
// why image embeds didn't appear. console keeps the detail; Notice is
// skipped in batch (quiet) mode.
if (markersBefore > 0 && markersAfter > 0) {
console.warn(
`[directoryTemplateService] ${markersAfter.toString()} of ${markersBefore.toString()} [IMAGE N: …] markers were not replaced. images.length=${images.length.toString()}, model=${modelName}.`
);
if (!quiet) {
new Notice(
`${markersAfter.toString()} image markers left unreplaced — Perplexity returned ${images.length.toString()} image(s).`
);
}
}
}
const sourcesFooter = buildSourcesFooter(sources);
const finalContent = `${initialContent}${cleanedStreamed}\n${sourcesFooter}`;